mirror of
https://github.com/tiennm99/coolify.git
synced 2026-08-19 22:23:42 +00:00
feat(v5): use bridged Lima hosts for coold dev
Add v5 architecture docs and update dev bootstrap, seeding, and tests to use bridged Lima .local hosts with a container-accessible SSH key.
This commit is contained in:
+2
-1
@@ -43,5 +43,6 @@ tests/v4/Browser/Screenshots
|
||||
|
||||
# Local generated Lima configs
|
||||
.dev/bin/
|
||||
.dev/lima/*.generated.yaml
|
||||
.dev/lima/ssh.config
|
||||
.dev/lima/ssh_key
|
||||
.dev/lima/hosts
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ return [
|
||||
'corrosion_version' => env('COOLIFY_CORROSION_VERSION', 'v1.0.0'),
|
||||
'dev_builder_capacity' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2),
|
||||
'dev_builder_enabled' => (int) env('COOLIFY_COOLD_VM_BUILDER_CAPACITY', 2) > 0,
|
||||
'dev_ssh_user' => env('COOLIFY_CLI_SSH_USER', get_current_user()),
|
||||
'dev_ssh_user' => env('COOLIFY_CLI_SSH_USER', 'coolify'),
|
||||
];
|
||||
|
||||
@@ -31,7 +31,7 @@ class V5DevLimaSeeder extends Seeder
|
||||
->first();
|
||||
|
||||
$builderCapacity = max(0, (int) config('coold.dev_builder_capacity', 2));
|
||||
$sshUser = (string) config('coold.dev_ssh_user', get_current_user());
|
||||
$sshUser = (string) config('coold.dev_ssh_user', 'coolify');
|
||||
$servers = collect($this->servers())
|
||||
->map(fn (array $server): array => [
|
||||
...$server,
|
||||
@@ -57,19 +57,19 @@ class V5DevLimaSeeder extends Seeder
|
||||
return [
|
||||
[
|
||||
'name' => 'coold-dev',
|
||||
'host' => 'host.docker.internal',
|
||||
'ssh_port' => 60001,
|
||||
'host' => 'coold-dev.local',
|
||||
'ssh_port' => 22,
|
||||
'wireguard_management_ip' => '100.64.0.1',
|
||||
'wireguard_listen_port_override' => 51821,
|
||||
'wireguard_endpoint_override' => 'host.lima.internal:51821',
|
||||
'wireguard_endpoint_override' => 'coold-dev.local:51821',
|
||||
],
|
||||
[
|
||||
'name' => 'coold-dev-2',
|
||||
'host' => 'host.docker.internal',
|
||||
'ssh_port' => 60002,
|
||||
'host' => 'coold-dev-2.local',
|
||||
'ssh_port' => 22,
|
||||
'wireguard_management_ip' => '100.64.0.2',
|
||||
'wireguard_listen_port_override' => 51822,
|
||||
'wireguard_endpoint_override' => 'host.lima.internal:51822',
|
||||
'wireguard_endpoint_override' => 'coold-dev-2.local:51822',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
+17
-13
@@ -49,20 +49,24 @@ scripts/dev.sh coolify bootstrap-command
|
||||
scripts/dev.sh coolify run <args>
|
||||
```
|
||||
|
||||
On macOS, the helper builds `coolify` from the local coold repo because the
|
||||
nightly release currently publishes Linux binaries. On Linux, it downloads the
|
||||
nightly release artifact.
|
||||
The helper runs the `coolify` CLI from the Coolify development container. Lima
|
||||
VMs are addressed by their bridged mDNS names (`<vm>.local`), so the same
|
||||
addresses are used by bootstrap and by the v5 dev server seeder.
|
||||
|
||||
The generated bootstrap command uses Lima's forwarded SSH ports and dev
|
||||
WireGuard endpoint overrides, for example:
|
||||
Because Docker Desktop does not reliably pass mDNS multicast into containers,
|
||||
`scripts/dev.sh up` resolves each `<vm>.local` name on the host and writes the
|
||||
resolved records into the Coolify container `/etc/hosts` before bootstrapping.
|
||||
|
||||
The generated bootstrap command uses the container CLI, the repo-local copy of
|
||||
the Lima SSH key, and dev WireGuard endpoint overrides, for example:
|
||||
|
||||
```bash
|
||||
.dev/bin/coolify init bootstrap \
|
||||
--nodes "127.0.0.1:<ssh1>,127.0.0.1:<ssh2>" \
|
||||
--ssh-key "$HOME/.lima/_config/user" \
|
||||
--ssh-user "$USER" \
|
||||
--wg-listen-port-overrides "127.0.0.1:<ssh1>=51821,127.0.0.1:<ssh2>=51822" \
|
||||
--wg-endpoint-overrides "127.0.0.1:<ssh1>=host.lima.internal:51821,127.0.0.1:<ssh2>=host.lima.internal:51822" \
|
||||
spin exec -T coolify /usr/local/bin/coolify init bootstrap \
|
||||
--nodes "coold-dev.local,coold-dev-2.local" \
|
||||
--ssh-key "/var/www/html/.dev/lima/ssh_key" \
|
||||
--ssh-user "coolify" \
|
||||
--wg-listen-port-overrides "coold-dev.local=51821,coold-dev-2.local=51822" \
|
||||
--wg-endpoint-overrides "coold-dev.local=coold-dev.local:51821,coold-dev-2.local=coold-dev-2.local:51822" \
|
||||
--coold-version "nightly" \
|
||||
--corrosion-version "v1.0.0" \
|
||||
--yes
|
||||
@@ -78,8 +82,8 @@ After `coolify init bootstrap`, defaults are:
|
||||
|
||||
| VM | WireGuard IP | WireGuard endpoint | Podman subnet | Gateway |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `coold-dev` | `100.64.0.1` | `host.lima.internal:51821` | `10.210.0.0/24` | `10.210.0.1` |
|
||||
| `coold-dev-2` | `100.64.0.2` | `host.lima.internal:51822` | `10.210.1.0/24` | `10.210.1.1` |
|
||||
| `coold-dev` | `100.64.0.1` | `coold-dev.local:51821` | `10.210.0.0/24` | `10.210.0.1` |
|
||||
| `coold-dev-2` | `100.64.0.2` | `coold-dev-2.local:51822` | `10.210.1.0/24` | `10.210.1.1` |
|
||||
|
||||
## Checking state
|
||||
|
||||
|
||||
+36
-62
@@ -1,79 +1,53 @@
|
||||
# Lima VM for testing Coolify v5 against coolify-provisioned hosts.
|
||||
# Start with: scripts/coold-vm.sh up
|
||||
#
|
||||
# This file is copied into .dev/lima/coold.generated.yaml by the wrapper script,
|
||||
# with {{COOLIFY_REPO}}, {{COOLIFY_COOLD_VERSION}}, and {{COOLIFY_CORROSION_VERSION}} replaced before Lima sees it.
|
||||
|
||||
vmType: "vz"
|
||||
arch: "default"
|
||||
user:
|
||||
name: coolify
|
||||
cpus: 4
|
||||
memory: "8GiB"
|
||||
disk: "100GiB"
|
||||
memory: "2GiB"
|
||||
disk: "20GiB"
|
||||
containerd:
|
||||
system: false
|
||||
user: false
|
||||
|
||||
ssh:
|
||||
localPort: {{COOLIFY_COOLD_VM_SSH_PORT}}
|
||||
|
||||
images:
|
||||
- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-amd64.img"
|
||||
- location: "https://cloud-images.ubuntu.com/releases/26.04/release/ubuntu-26.04-server-cloudimg-amd64.img"
|
||||
arch: "x86_64"
|
||||
- location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img"
|
||||
- location: "https://cloud-images.ubuntu.com/releases/26.04/release/ubuntu-26.04-server-cloudimg-arm64.img"
|
||||
arch: "aarch64"
|
||||
|
||||
mountType: "virtiofs"
|
||||
mounts:
|
||||
- location: "{{COOLIFY_REPO}}"
|
||||
mountPoint: "/workspace/coolify"
|
||||
writable: true
|
||||
|
||||
portForwards:
|
||||
- guestPort: 5173
|
||||
hostPort: 5173
|
||||
hostIP: "127.0.0.1"
|
||||
- guestPort: 3000
|
||||
hostPort: 3000
|
||||
hostIP: "127.0.0.1"
|
||||
networks:
|
||||
- lima: bridged
|
||||
|
||||
provision:
|
||||
- mode: system
|
||||
script: |
|
||||
#!/usr/bin/env bash
|
||||
set -euxo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
echo "[coold-vm] Updating Ubuntu package index..."
|
||||
apt-get update
|
||||
echo "[coold-vm] Installing minimal VM dependencies..."
|
||||
apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
iproute2 \
|
||||
jq \
|
||||
openssl \
|
||||
sqlite3 \
|
||||
sudo
|
||||
cat >/usr/local/bin/rtk <<'SH'
|
||||
#!/usr/bin/env bash
|
||||
exec "$@"
|
||||
SH
|
||||
chmod +x /usr/local/bin/rtk
|
||||
mkdir -p /etc/coolify /var/lib/coolify-dev
|
||||
chmod 755 /etc/coolify
|
||||
|
||||
coolify_test_public_key="ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFuGmoeGq/pojrsyP1pszcNVuZx9iFkCELtxrh31QJ68 sail@76ff66d2e2dd"
|
||||
install -d -m 700 /root/.ssh
|
||||
touch /root/.ssh/authorized_keys
|
||||
grep -qxF "$coolify_test_public_key" /root/.ssh/authorized_keys || echo "$coolify_test_public_key" >>/root/.ssh/authorized_keys
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
target_home="$(getent passwd 501 | cut -d: -f6 || true)"
|
||||
if [ -n "$target_home" ] && [ -d "$target_home" ]; then
|
||||
owner="$(stat -c '%u:%g' "$target_home")"
|
||||
install -d -m 700 -o "${owner%:*}" -g "${owner#*:}" "$target_home/.ssh"
|
||||
touch "$target_home/.ssh/authorized_keys"
|
||||
grep -qxF "$coolify_test_public_key" "$target_home/.ssh/authorized_keys" || echo "$coolify_test_public_key" >>"$target_home/.ssh/authorized_keys"
|
||||
chown "$owner" "$target_home/.ssh/authorized_keys"
|
||||
chmod 600 "$target_home/.ssh/authorized_keys"
|
||||
fi
|
||||
|
||||
echo "[coold-vm] Minimal provisioning complete. coolify bootstrap will install WireGuard, Podman, Corrosion, coold, and builder."
|
||||
|
||||
echo "[coold-vm] Installing and configuring mDNS."
|
||||
apt-get update
|
||||
apt-get install -y avahi-daemon
|
||||
hostnamectl set-hostname "{{.Name}}"
|
||||
systemctl enable --now avahi-daemon
|
||||
echo "[coold-vm] Installing SSH keys."
|
||||
|
||||
install_public_key() {
|
||||
user="$1"
|
||||
home="$(getent passwd "$user" | cut -d: -f6 || true)"
|
||||
if [ -z "$home" ] || [ ! -d "$home" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
owner="$(stat -c '%u:%g' "$home")"
|
||||
authorized_keys="$home/.ssh/authorized_keys"
|
||||
install -d -m 700 -o "${owner%:*}" -g "${owner#*:}" "$home/.ssh"
|
||||
touch "$authorized_keys"
|
||||
chown "$owner" "$authorized_keys"
|
||||
chmod 600 "$authorized_keys"
|
||||
grep -qxF "$coolify_test_public_key" "$authorized_keys" || echo "$coolify_test_public_key" >>"$authorized_keys"
|
||||
chown "$owner" "$authorized_keys"
|
||||
}
|
||||
|
||||
install_public_key root
|
||||
install_public_key coolify
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Coolify v5 Architecture
|
||||
|
||||
This directory is the canonical architecture reference for Coolify v5 from the
|
||||
Laravel control-plane point of view.
|
||||
|
||||
v5 has three major building blocks:
|
||||
|
||||
```text
|
||||
User / API / Git webhook
|
||||
↓
|
||||
Coolify Laravel control plane
|
||||
↓ HTTP over /run/coolify/flux.sock
|
||||
Flux
|
||||
↓ outbound gRPC Agent.Stream
|
||||
coold on each host
|
||||
↓
|
||||
Podman / networks / firewall / DNS / Corrosion / builder
|
||||
```
|
||||
|
||||
## Documents
|
||||
|
||||
| Document | Purpose |
|
||||
| --- | --- |
|
||||
| [Overview](overview.md) | High-level mental model and data flow. |
|
||||
| [Responsibility split](responsibility-split.md) | What belongs in Coolify, Flux, and coold. |
|
||||
| [Primitives](primitives.md) | Canonical host primitive surface Coolify may dispatch. |
|
||||
| [Deploy flows](deploy-flows.md) | User-functionality flows, starting with `nginx:alpine`. |
|
||||
| [ADR 0001](adr/0001-control-plane-flux-coold-split.md) | Decision record for the v5 split. |
|
||||
|
||||
## Core rule
|
||||
|
||||
Coolify owns user intent and application state. Flux routes requests. coold
|
||||
executes explicit host primitives. coold must not grow app, team, RBAC,
|
||||
deployment, billing, or audit concepts.
|
||||
@@ -0,0 +1,75 @@
|
||||
# ADR 0001: Split v5 into Coolify control plane, Flux broker, and coold host agent
|
||||
|
||||
## Status
|
||||
|
||||
Accepted.
|
||||
|
||||
## Context
|
||||
|
||||
Coolify v5 needs to manage many user resources across many user-owned hosts,
|
||||
including hosts behind NAT or restrictive firewalls. The Laravel application
|
||||
must own product behavior and durable state, but it should not hold thousands of
|
||||
long-lived agent streams or directly expose host runtime sockets.
|
||||
|
||||
Host operations also need a narrow privileged boundary. Podman, firewall, DNS,
|
||||
Corrosion, and build supervision require local host privileges that should not
|
||||
be spread across the Laravel app or arbitrary scripts.
|
||||
|
||||
## Decision
|
||||
|
||||
Coolify v5 uses three distinct building blocks:
|
||||
|
||||
1. **Coolify Laravel control plane** owns user intent, durable product state,
|
||||
RBAC, deployment state machines, placement decisions, secrets, proxy config
|
||||
rendering, notifications, and audit.
|
||||
2. **Flux** owns long-lived agent connectivity. Laravel talks to Flux over a
|
||||
Unix socket. coold agents dial Flux over outbound gRPC. Flux routes typed
|
||||
primitive requests to connected hosts and resolves pending responses.
|
||||
3. **coold** runs once per host. It exposes a closed set of host primitives and
|
||||
owns privileged local execution through Podman, firewall, DNS, Corrosion,
|
||||
host facts, and builder supervision.
|
||||
|
||||
coold must not expose raw Podman passthrough. Every supported operation must be
|
||||
an explicit primitive with validation and a stable protocol shape.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Coolify keeps product complexity in one durable control plane.
|
||||
- Hosts can live behind NAT because coold dials out to Flux.
|
||||
- Privileged host access is isolated to coold.
|
||||
- Flux can scale connection handling separately from Laravel request workers.
|
||||
- The primitive surface can be tested independently from Coolify's app model.
|
||||
|
||||
### Negative
|
||||
|
||||
- More moving parts than a direct SSH/Docker model.
|
||||
- Protocol changes require coordination across Coolify, Flux, and coold.
|
||||
- Some deploy features require new primitives before the v5 path is complete.
|
||||
|
||||
## Boundary rules
|
||||
|
||||
- Coolify may say: "deploy this nginx resource for this team to host H1." coold
|
||||
may not.
|
||||
- Coolify may send: `images.pull`, `containers.create`, `containers.start`,
|
||||
`services.register`, `firewall.allow`. coold may execute those.
|
||||
- Flux may route requests but may not make product decisions.
|
||||
- coold may reject dangerous host operations even when Coolify requested them.
|
||||
|
||||
## First reference flow
|
||||
|
||||
The first validation flow is deploying a Docker image app using `nginx:alpine`.
|
||||
That flow requires no builder and exercises the minimum runtime path:
|
||||
|
||||
```text
|
||||
Coolify state machine
|
||||
→ Flux dispatch
|
||||
→ coold image pull
|
||||
→ coold container create/start
|
||||
→ coold status
|
||||
→ coold service registration
|
||||
→ coold firewall
|
||||
→ Coolify proxy config/reload
|
||||
→ Coolify marks deployment running
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# v5 Deploy Flows
|
||||
|
||||
These flows are written from the user-functionality view. Coolify owns the
|
||||
state machine; Flux routes primitive requests; coold executes host operations.
|
||||
|
||||
## Flow: deploy Docker image app `nginx:alpine`
|
||||
|
||||
User intent:
|
||||
|
||||
```text
|
||||
Run nginx:alpine on host H1, expose port 80, and route nginx.example.com to it.
|
||||
```
|
||||
|
||||
### Steps
|
||||
|
||||
| Step | Coolify | Flux | coold |
|
||||
| --- | --- | --- | --- |
|
||||
| Create app | Stores app, source type `docker_image`, image `nginx:alpine`, target host, port, domain. | No action. | No action. |
|
||||
| Start deploy | Creates deployment record and enters deploy state machine. | No action until dispatch. | No action. |
|
||||
| Pull image | Sends `images.pull` to H1. | Routes request to H1's stream. | Pulls image through Podman. |
|
||||
| Prepare network/volume | Sends required `networks.create` / `volumes.create` operations. | Routes requests. | Creates idempotent host resources. |
|
||||
| Create container | Sends `containers.create` with image, name, env, labels, network, port, health check, mounts, DNS. | Routes request. | Applies deny filter and creates container through Podman. |
|
||||
| Start container | Sends `containers.start`. | Routes request. | Starts container. |
|
||||
| Check status | Sends `containers.inspect` or reads status stream. | Routes request. | Reads local Podman state. |
|
||||
| Register endpoint | Sends `services.register`. | Routes request. | Writes this host's endpoint row to Corrosion. |
|
||||
| Allow traffic | Sends `firewall.allow` for proxy-to-container traffic when required. | Routes request. | Writes iptables and nft allow rules. |
|
||||
| Configure ingress | Renders proxy config from Coolify domain/resource state. If reload is needed, sends `containers.exec` or equivalent primitive for the proxy container. | Routes runtime reload request. | Executes the reload primitive only. |
|
||||
| Mark running | Stores final status, deployment result, and user-facing logs. | Resolves final responses. | Keeps container running and reports future state. |
|
||||
|
||||
### Expected data locations
|
||||
|
||||
| Data | Owner |
|
||||
| --- | --- |
|
||||
| App name, team, project, env, domain, image ref, desired port | Coolify database |
|
||||
| Deployment state and history | Coolify database |
|
||||
| Open host stream and pending request IDs | Flux memory |
|
||||
| Pulled image and running container | Host Podman storage |
|
||||
| Endpoint rows for this host | Corrosion via coold |
|
||||
| Firewall tuples and snapshots | Host kernel + `/etc/coolify` via coold |
|
||||
|
||||
### No build path
|
||||
|
||||
`nginx:alpine` is a prebuilt image. This flow does not use Git clone,
|
||||
Dockerfile, buildpacks, Railpack, or the builder subprocess.
|
||||
|
||||
## Flow: delete the nginx app
|
||||
|
||||
| Step | Coolify | Flux | coold |
|
||||
| --- | --- | --- | --- |
|
||||
| User deletes resource | Validates permission and starts cleanup state machine. | No action until dispatch. | No action. |
|
||||
| Stop container | Sends `containers.stop`. | Routes request. | Stops container. |
|
||||
| Remove container | Sends `containers.delete`. | Routes request. | Deletes container. |
|
||||
| Remove endpoint | Sends `services.unregister`. | Routes request. | Removes service endpoint row. |
|
||||
| Remove firewall rule | Sends `firewall.revoke`. | Routes request. | Removes iptables and nft allow rules. |
|
||||
| Update proxy | Removes rendered route and reloads proxy if needed. | Routes reload primitive. | Executes proxy reload primitive. |
|
||||
| Finish cleanup | Marks resource deleted and records outcome. | Resolves responses. | No product state retained. |
|
||||
|
||||
## Flow: Git app with build
|
||||
|
||||
A Git app adds a build phase before container creation.
|
||||
|
||||
```text
|
||||
Coolify resolves source + secrets + build config
|
||||
↓
|
||||
Flux routes build request to a host with builder capability
|
||||
↓
|
||||
coold supervises builder subprocess
|
||||
↓
|
||||
builder writes image/result
|
||||
↓
|
||||
Coolify deploy state machine continues with image/container primitives
|
||||
```
|
||||
|
||||
coold supervises the builder process. The builder owns build implementation.
|
||||
Coolify owns the decision to build, the build configuration, and the deployment
|
||||
state transitions around the build.
|
||||
@@ -0,0 +1,38 @@
|
||||
# v5 Architecture Overview
|
||||
|
||||
Coolify v5 separates product intent from host execution.
|
||||
|
||||
- **Coolify Laravel** is the control plane. It owns users, teams, projects,
|
||||
environments, resources, deployments, domains, secrets, RBAC, audit-worthy
|
||||
state, and the deployment state machines.
|
||||
- **Flux** is the connection broker. Laravel talks to Flux over a Unix socket.
|
||||
coold agents dial Flux over outbound gRPC streams. Flux maps host IDs to
|
||||
connected streams and maps request IDs to pending responses.
|
||||
- **coold** is the per-host executor. It owns local runtime access: Podman,
|
||||
host networks, DNS, firewall mutations, service-discovery sync, host facts,
|
||||
and builder subprocess supervision.
|
||||
|
||||
## Data flow
|
||||
|
||||
```text
|
||||
1. A user/API/webhook asks Coolify to do something.
|
||||
2. Coolify validates permissions and stores desired state in its database.
|
||||
3. Coolify's state machine turns that intent into ordered host primitives.
|
||||
4. Coolify sends each primitive to Flux over /run/coolify/flux.sock.
|
||||
5. Flux forwards the primitive to the target host's open coold stream.
|
||||
6. coold executes the primitive locally and returns a typed result.
|
||||
7. Flux resolves the pending request.
|
||||
8. Coolify records the result and moves the deployment/resource state forward.
|
||||
```
|
||||
|
||||
## Non-goals for Flux and coold
|
||||
|
||||
Flux and coold are not product layers. They do not decide which app to deploy,
|
||||
which user is allowed to deploy, how to roll back, what domains mean, or where
|
||||
business audit belongs. They only handle routing and host execution.
|
||||
|
||||
## Current implementation note
|
||||
|
||||
This directory describes the target v5 architecture. Some primitives are not
|
||||
implemented in coold yet. Until implemented, docs should label them as target
|
||||
primitives and code should not pretend they exist.
|
||||
@@ -0,0 +1,106 @@
|
||||
# v5 Host Primitives
|
||||
|
||||
Coolify deploy logic must send explicit primitives to coold through Flux. There
|
||||
must be no raw Podman passthrough.
|
||||
|
||||
This file is the target primitive catalog. Implementations may lag behind this
|
||||
catalog; code should only call primitives that exist in the current protocol.
|
||||
|
||||
## Images
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `images.pull` | Pull an OCI image ref onto a host. |
|
||||
| `images.list` | List local images. |
|
||||
| `images.delete` | Remove a local image when safe. |
|
||||
|
||||
## Containers
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `containers.create` | Create a container from an explicit spec. |
|
||||
| `containers.start` | Start an existing container. |
|
||||
| `containers.stop` | Stop a container with an optional timeout. |
|
||||
| `containers.restart` | Restart a container. |
|
||||
| `containers.delete` | Delete a stopped or forced container. |
|
||||
| `containers.inspect` | Return detailed runtime state. |
|
||||
| `containers.list` | Return host container summaries. |
|
||||
| `containers.logs` | Stream or read container logs. |
|
||||
| `containers.exec` | Run a command inside a container. |
|
||||
| `containers.healthcheck.run` | Trigger or read a runtime health check. |
|
||||
|
||||
`containers.create` must enforce a deny filter for dangerous host options such
|
||||
as privileged mode, unsafe host mounts, host networking, and disallowed
|
||||
capabilities unless the host is explicitly configured to allow them.
|
||||
|
||||
## Volumes
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `volumes.create` | Create an idempotent named volume. |
|
||||
| `volumes.inspect` | Inspect a host volume. |
|
||||
| `volumes.delete` | Delete an unused volume. |
|
||||
|
||||
## Networks
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `networks.create` | Create an idempotent Podman network. |
|
||||
| `networks.list` | List host networks. |
|
||||
| `networks.delete` | Delete an unused network. |
|
||||
|
||||
Bootstrap-created mesh namespace networks are managed by the v5 cluster init
|
||||
flow. Per-resource or compose networks are runtime primitives.
|
||||
|
||||
## Firewall
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `firewall.allow` | Add an allow tuple and persist it. |
|
||||
| `firewall.revoke` | Remove an allow tuple by ID. |
|
||||
| `firewall.list` | List active/persisted allow rules. |
|
||||
| `firewall.reconcile` | Flush and restore firewall state from snapshots. |
|
||||
|
||||
coold is the sole writer for both firewall planes: iptables for cross-host
|
||||
traffic and nft bridge rules for same-bridge traffic.
|
||||
|
||||
## Service discovery and DNS
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `services.register` | Register this host's container endpoint in Corrosion. |
|
||||
| `services.unregister` | Remove an endpoint from Corrosion. |
|
||||
| `services.endpoints` | Read known endpoints for diagnostics. |
|
||||
| `dns.lookup` | Diagnose internal DNS resolution. |
|
||||
| `dns.stats` | Return DNS server status. |
|
||||
|
||||
## Host facts
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `host.info` | Return host/runtime facts for scheduling and debugging. |
|
||||
| `host.stats` | Return CPU, memory, disk, and container stats snapshot. |
|
||||
| `host.containers` | Return host container summaries. |
|
||||
|
||||
## Builder
|
||||
|
||||
Builds are routed through Flux to a host with builder capability. coold should
|
||||
supervise the builder process but should not implement build logic inline.
|
||||
|
||||
| Primitive | Purpose |
|
||||
| --- | --- |
|
||||
| `build.dispatch` | Start a builder request on a capable host. |
|
||||
| `build.cancel` | Cancel a running build request. |
|
||||
| `build.result` | Return or long-poll build result. |
|
||||
|
||||
## Not primitives
|
||||
|
||||
These belong in Coolify, not coold:
|
||||
|
||||
- `deploy.application`
|
||||
- `rollback.deployment`
|
||||
- `create.preview`
|
||||
- `configure.domain`
|
||||
- `authorize.user`
|
||||
- `send.notification`
|
||||
- `render.proxy.config`
|
||||
@@ -0,0 +1,60 @@
|
||||
# v5 Responsibility Split
|
||||
|
||||
Use this as the first check when deciding where a new feature belongs.
|
||||
|
||||
## Rule of thumb
|
||||
|
||||
| Question | Owner |
|
||||
| --- | --- |
|
||||
| What should happen for this user, team, app, database, or deployment? | Coolify |
|
||||
| Which connected host should receive this request? | Coolify chooses, Flux routes |
|
||||
| How do we get a command to a NATed host? | Flux |
|
||||
| Do this concrete operation on this host. | coold |
|
||||
| Is this allowed for this Coolify user/team? | Coolify |
|
||||
| Is this host operation dangerous even if Coolify asked for it? | coold deny filter |
|
||||
|
||||
## Coolify owns
|
||||
|
||||
- Users, teams, roles, RBAC, API tokens, sessions, SSO/OAuth.
|
||||
- Projects, environments, applications, services, databases, servers.
|
||||
- Resource configuration: source, image, build settings, env vars, domains,
|
||||
ports, health checks, resource limits, volumes, schedules, webhooks.
|
||||
- Deployment state machines: pending, building, pulling, creating, starting,
|
||||
health waiting, cutover, running, failed, rollback, cleanup.
|
||||
- Placement and scheduling decisions.
|
||||
- Proxy/ingress configuration rendering and TLS intent.
|
||||
- Secret storage, encryption, resolution, and injection at deploy time.
|
||||
- Business audit, event history, deployment logs, and user-facing status.
|
||||
- Notifications, billing/subscriptions, cloud-provider integration.
|
||||
|
||||
## Flux owns
|
||||
|
||||
- Long-lived coold stream registry keyed by host ID.
|
||||
- Pending request registry keyed by request ID.
|
||||
- Request routing from Laravel's Unix-socket lane to the selected coold stream.
|
||||
- Timeouts, disconnected-host responses, pending-cap protection, and late result
|
||||
handling.
|
||||
- Host-agent authentication for inbound coold streams.
|
||||
|
||||
Flux does not inspect product meaning. `containers.start` is just a frame to a
|
||||
host; Flux does not know it is part of an nginx deployment.
|
||||
|
||||
## coold owns
|
||||
|
||||
- Local Podman access.
|
||||
- Host runtime primitives: images, containers, volumes, networks, logs, exec,
|
||||
health checks, host facts.
|
||||
- Firewall mutation and reconciliation as the sole kernel firewall writer.
|
||||
- Embedded DNS and service-discovery sync.
|
||||
- Writing this host's service endpoint rows to Corrosion.
|
||||
- Builder subprocess supervision when the host advertises builder capability.
|
||||
- Host-level safety checks such as container-create deny filters.
|
||||
|
||||
coold does not store Coolify secrets, users, teams, app ownership, deployment
|
||||
history, billing data, or business audit.
|
||||
|
||||
## Boundary test
|
||||
|
||||
If a different orchestrator could reuse the same operation with its own app
|
||||
model, it probably belongs in coold. If the operation only makes sense because
|
||||
of Coolify's product model, it belongs in Coolify.
|
||||
+15
-22
@@ -30,7 +30,6 @@ CORROSION_VERSION="$(read_coolify_env COOLIFY_CORROSION_VERSION v1.0.0)"
|
||||
FLUX_URL="$(read_coolify_env COOLIFY_COOLD_VM_FLUX_URL http://host.lima.internal:6443)"
|
||||
BUILDER_CAPACITY="$(read_coolify_env COOLIFY_COOLD_VM_BUILDER_CAPACITY 2)"
|
||||
START_TIMEOUT="$(read_coolify_env COOLIFY_COOLD_VM_START_TIMEOUT 300)"
|
||||
SSH_PORT="$(read_coolify_env COOLIFY_COOLD_VM_SSH_PORT 60002)"
|
||||
WG_IP="$(read_coolify_env COOLIFY_COOLD_VM_WG_IP "")"
|
||||
WG_PEER_IP="$(read_coolify_env COOLIFY_COOLD_VM_WG_PEER_IP "")"
|
||||
WG_PEER_ENDPOINT="$(read_coolify_env COOLIFY_COOLD_VM_WG_PEER_ENDPOINT "")"
|
||||
@@ -42,7 +41,6 @@ if [ "$BUILDER_CAPACITY" = "0" ]; then
|
||||
BUILDER_ENABLED="false"
|
||||
fi
|
||||
TEMPLATE="$ROOT/dev/lima/coold.yaml"
|
||||
GENERATED="$ROOT/.dev/lima/${INSTANCE}.generated.yaml"
|
||||
GUEST_COOLIFY_ROOT="/workspace/coolify"
|
||||
|
||||
usage() {
|
||||
@@ -72,7 +70,6 @@ Environment:
|
||||
COOLIFY_COOLD_VM_FLUX_URL Flux gRPC URL visible from the VM (default: http://host.lima.internal:6443)
|
||||
COOLIFY_COOLD_VM_BUILDER_CAPACITY VM builder capacity to advertise (default: 2; set 0 to disable)
|
||||
COOLIFY_COOLD_VM_START_TIMEOUT Seconds to wait for Lima SSH/provisioning (default: 300)
|
||||
COOLIFY_COOLD_VM_SSH_PORT Host SSH port forwarded to this VM (default: 60002)
|
||||
COOLIFY_COOLD_VM_WG_IP Optional WireGuard mgmt IP for this host
|
||||
COOLIFY_COOLD_VM_CONTAINER_SUBNET Podman mesh subnet for this host
|
||||
COOLIFY_COOLD_VM_CONTAINER_GATEWAY Podman mesh gateway for this host
|
||||
@@ -341,8 +338,6 @@ COOLIFY_COOLD_PODMAN_SOCKET="${COOLIFY_COOLD_PODMAN_SOCKET:-/run/podman/podman.s
|
||||
COOLIFY_COOLD_CORROSION_URL="${COOLIFY_COOLD_CORROSION_URL:-http://127.0.0.1:8080}" \
|
||||
COOLIFY_COOLD_NAMESPACES="${COOLIFY_COOLD_NAMESPACES:-default:coolify-default-mesh:$CONTAINER_GATEWAY}" \
|
||||
COOLIFY_COOLD_DNS_ZONE="${COOLIFY_COOLD_DNS_ZONE:-coolify.internal}" \
|
||||
COOLIFY_COOLD_API_BIND="${COOLIFY_COOLD_API_BIND:-${WG_IP:-127.0.0.1}:8443}" \
|
||||
COOLIFY_COOLD_API_TOKEN_FILE="${COOLIFY_COOLD_API_TOKEN_FILE:-/etc/coolify/api-token}" \
|
||||
COOLIFY_COOLD_FLUX_URL="${COOLIFY_COOLD_FLUX_URL:-http://host.lima.internal:6443}" \
|
||||
COOLIFY_COOLD_HOST_JWT_PATH="${COOLIFY_COOLD_HOST_JWT_PATH:-/etc/coolify/host-jwt}" \
|
||||
COOLIFY_COOLD_BUILDER_ENABLED="${COOLIFY_COOLD_BUILDER_ENABLED:-true}" \
|
||||
@@ -417,7 +412,6 @@ start_agent() {
|
||||
configure_system_resolved
|
||||
ensure_mesh_dns_anchor
|
||||
install_mesh_firewall
|
||||
lima_shell sudo sh -c 'if [ ! -s /etc/coolify/api-token ]; then openssl rand -hex 32 > /etc/coolify/api-token.tmp && chmod 600 /etc/coolify/api-token.tmp && mv /etc/coolify/api-token.tmp /etc/coolify/api-token; fi'
|
||||
|
||||
lima_shell sudo tee /etc/systemd/system/corrosion.service >/dev/null <<'UNIT'
|
||||
[Unit]
|
||||
@@ -446,8 +440,6 @@ Environment=COOLIFY_COOLD_PODMAN_SOCKET=/run/podman/podman.sock
|
||||
Environment=COOLIFY_COOLD_CORROSION_URL=http://127.0.0.1:8080
|
||||
Environment=COOLIFY_COOLD_NAMESPACES=default:coolify-default-mesh:$CONTAINER_GATEWAY
|
||||
Environment=COOLIFY_COOLD_DNS_ZONE=coolify.internal
|
||||
Environment=COOLIFY_COOLD_API_BIND=${WG_IP:-127.0.0.1}:8443
|
||||
Environment=COOLIFY_COOLD_API_TOKEN_FILE=/etc/coolify/api-token
|
||||
Environment=COOLIFY_COOLD_FLUX_URL=$FLUX_URL
|
||||
Environment=COOLIFY_COOLD_HOST_JWT_PATH=/etc/coolify/host-jwt
|
||||
Environment=COOLIFY_COOLD_BUILDER_ENABLED=$BUILDER_ENABLED
|
||||
@@ -468,19 +460,7 @@ UNIT
|
||||
lima_shell sudo systemctl restart corrosion.service coold.service
|
||||
}
|
||||
|
||||
generate_yaml() {
|
||||
mkdir -p "$(dirname "$GENERATED")"
|
||||
sed \
|
||||
-e "s#{{COOLIFY_REPO}}#$ROOT#g" \
|
||||
-e "s#{{COOLIFY_COOLD_VERSION}}#$VERSION#g" \
|
||||
-e "s#{{COOLIFY_CORROSION_VERSION}}#$CORROSION_VERSION#g" \
|
||||
-e "s#{{COOLIFY_COOLD_VM_SSH_PORT}}#$SSH_PORT#g" \
|
||||
"$TEMPLATE" > "$GENERATED"
|
||||
}
|
||||
|
||||
start_vm() {
|
||||
generate_yaml
|
||||
|
||||
if instance_running; then
|
||||
return
|
||||
fi
|
||||
@@ -491,7 +471,7 @@ start_vm() {
|
||||
if instance_exists; then
|
||||
limactl start --tty=false "$INSTANCE"
|
||||
else
|
||||
limactl start --tty=false --name="$INSTANCE" "$GENERATED"
|
||||
limactl start --tty=false --name="$INSTANCE" "$TEMPLATE"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -604,14 +584,27 @@ wait_for_guest_provisioning() {
|
||||
| awk '{ print "[guest] " $0; fflush(); }' || true
|
||||
}
|
||||
|
||||
ensure_mdns_hostname() {
|
||||
local current_hostname
|
||||
current_hostname="$(lima_shell hostname 2>/dev/null || true)"
|
||||
|
||||
if [ "$current_hostname" != "$INSTANCE" ]; then
|
||||
echo "==> Setting guest hostname to ${INSTANCE} for ${INSTANCE}.local mDNS..."
|
||||
lima_shell sudo hostnamectl set-hostname "$INSTANCE"
|
||||
fi
|
||||
|
||||
lima_shell sudo systemctl restart avahi-daemon.service
|
||||
}
|
||||
|
||||
up_with_logs() {
|
||||
echo "==> Coolify coold VM: $INSTANCE"
|
||||
echo "==> coold package tag: $VERSION"
|
||||
echo "==> corrosion package tag: $CORROSION_VERSION"
|
||||
echo "==> Lima config: $GENERATED"
|
||||
echo "==> Lima config: $TEMPLATE"
|
||||
|
||||
wait_for_lima_start
|
||||
wait_for_guest_provisioning
|
||||
ensure_mdns_hostname
|
||||
|
||||
echo "==> VM is ready. Run coolify bootstrap via: scripts/dev.sh up"
|
||||
}
|
||||
|
||||
+104
-315
@@ -60,9 +60,71 @@ coold_vm_wg_port() {
|
||||
read_coolify_env "COOLIFY_COOLD_VM_WG_PORT_${index}" "$((51820 + index))"
|
||||
}
|
||||
|
||||
coold_vm_ssh_port() {
|
||||
coold_vm_dns_name() {
|
||||
local index="$1"
|
||||
read_coolify_env "COOLIFY_COOLD_VM_SSH_PORT_${index}" "6000${index}"
|
||||
printf '%s.local\n' "$(coold_vm_instance "$index")"
|
||||
}
|
||||
|
||||
resolve_lima_dns_name() {
|
||||
local name="$1"
|
||||
local ip=""
|
||||
|
||||
if command -v dscacheutil >/dev/null 2>&1; then
|
||||
ip="$(dscacheutil -q host -a name "$name" 2>/dev/null | awk '/ip_address:/ && $2 ~ /^[0-9.]+$/ { print $2; exit }')"
|
||||
fi
|
||||
|
||||
if [ -z "$ip" ] && command -v getent >/dev/null 2>&1; then
|
||||
ip="$(getent ahostsv4 "$name" 2>/dev/null | awk '{ print $1; exit }')"
|
||||
fi
|
||||
|
||||
if [ -z "$ip" ]; then
|
||||
ip="$(ping -c 1 -W 1 "$name" 2>/dev/null | sed -n 's/^PING .* (\([0-9.]*\)).*$/\1/p' | head -n 1)"
|
||||
fi
|
||||
|
||||
if [ -z "$ip" ]; then
|
||||
echo "ERROR: Could not resolve ${name} from the host." >&2
|
||||
echo "Check that the Lima VM is running, bridged networking is enabled, and Avahi/mDNS finished provisioning." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$ip"
|
||||
}
|
||||
|
||||
sync_lima_hosts_into_coolify_container() {
|
||||
local count
|
||||
local hosts_file="$ROOT/.dev/lima/hosts"
|
||||
local name
|
||||
local ip
|
||||
|
||||
count="$(coold_vm_count)"
|
||||
mkdir -p "$(dirname "$hosts_file")"
|
||||
: > "$hosts_file"
|
||||
|
||||
for index in $(seq 1 "$count"); do
|
||||
name="$(coold_vm_dns_name "$index")"
|
||||
ip="$(resolve_lima_dns_name "$name")" || return 1
|
||||
printf '%s %s\n' "$ip" "$name" >> "$hosts_file"
|
||||
done
|
||||
|
||||
echo "==> Syncing Lima .local host records into the Coolify container..."
|
||||
spin exec -T -u root coolify sh -lc '
|
||||
set -e
|
||||
records=/tmp/coolify-lima-hosts
|
||||
next=/tmp/coolify-hosts-next
|
||||
cat > "$records"
|
||||
cp /etc/hosts "$next"
|
||||
while read -r ip name; do
|
||||
if [ -z "$ip" ] || [ -z "$name" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
awk -v name="$name" '\''$0 !~ ("(^|[[:space:]])" name "([[:space:]]|$)") { print }'\'' "$next" > "${next}.filtered"
|
||||
printf "%s %s\n" "$ip" "$name" >> "${next}.filtered"
|
||||
mv "${next}.filtered" "$next"
|
||||
done < "$records"
|
||||
cat "$next" > /etc/hosts
|
||||
rm -f "$records" "$next" "${next}.filtered"
|
||||
' < "$hosts_file"
|
||||
}
|
||||
|
||||
coold_vm_container_subnet() {
|
||||
@@ -75,124 +137,13 @@ coold_vm_container_gateway() {
|
||||
read_coolify_env "COOLIFY_COOLD_VM_CONTAINER_GATEWAY_${index}" "10.210.$((index - 1)).1"
|
||||
}
|
||||
|
||||
host_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64|amd64)
|
||||
printf '%s\n' amd64
|
||||
;;
|
||||
arm64|aarch64)
|
||||
printf '%s\n' arm64
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unsupported host architecture: $(uname -m)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
host_os() {
|
||||
case "$(uname -s)" in
|
||||
Darwin)
|
||||
printf '%s\n' darwin
|
||||
;;
|
||||
Linux)
|
||||
printf '%s\n' linux
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unsupported host OS: $(uname -s)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
coolify_cli_bin() {
|
||||
printf '%s\n' "$ROOT/.dev/bin/coolify"
|
||||
}
|
||||
|
||||
ensure_coolify() {
|
||||
local bin
|
||||
local version
|
||||
local arch
|
||||
local os
|
||||
local url
|
||||
local tmpdir
|
||||
|
||||
bin="$(coolify_cli_bin)"
|
||||
version="$(read_coolify_env COOLIFY_CLI_VERSION "$(read_coolify_env COOLIFY_COOLD_VERSION nightly)")"
|
||||
arch="$(host_arch)"
|
||||
os="$(host_os)"
|
||||
|
||||
url="https://github.com/coollabsio/coold/releases/download/${version}/coolify-${os}-${arch}.tar.gz"
|
||||
echo "==> Installing coolify from ${url}"
|
||||
|
||||
mkdir -p "$(dirname "$bin")"
|
||||
tmpdir="$(mktemp -d)"
|
||||
if ! curl -fsSL --retry 3 --max-time 120 -o "$tmpdir/coolify.tar.gz" "$url"; then
|
||||
rm -rf "$tmpdir"
|
||||
return 1
|
||||
fi
|
||||
tar -xzf "$tmpdir/coolify.tar.gz" -C "$tmpdir"
|
||||
install -m 0755 "$tmpdir/coolify" "$bin"
|
||||
rm -rf "$tmpdir"
|
||||
printf '%s\n' '/usr/local/bin/coolify'
|
||||
}
|
||||
|
||||
lima_ssh_target() {
|
||||
local index="$1"
|
||||
local instance
|
||||
|
||||
instance="$(coold_vm_instance "$index")"
|
||||
|
||||
if [ ! -f "$HOME/.lima/${instance}/ssh.config" ]; then
|
||||
echo "ERROR: Lima SSH config for ${instance} was not found. Start it first with scripts/dev.sh up." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'lima-%s\n' "$instance"
|
||||
}
|
||||
|
||||
lima_ssh_config() {
|
||||
local count
|
||||
local config="$ROOT/.dev/lima/ssh.config"
|
||||
local instance
|
||||
count="$(coold_vm_count)"
|
||||
|
||||
mkdir -p "$(dirname "$config")"
|
||||
: > "$config"
|
||||
|
||||
for index in $(seq 1 "$count"); do
|
||||
instance="$(coold_vm_instance "$index")"
|
||||
if [ ! -f "$HOME/.lima/${instance}/ssh.config" ]; then
|
||||
echo "ERROR: Lima SSH config for ${instance} was not found. Start it first with scripts/dev.sh up." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat "$HOME/.lima/${instance}/ssh.config" >> "$config"
|
||||
printf '\n' >> "$config"
|
||||
done
|
||||
|
||||
printf '%s\n' "$config"
|
||||
}
|
||||
|
||||
lima_ssh_port() {
|
||||
local index="$1"
|
||||
local instance
|
||||
local port
|
||||
|
||||
instance="$(coold_vm_instance "$index")"
|
||||
|
||||
if [ ! -f "$HOME/.lima/${instance}/ssh.config" ]; then
|
||||
echo "ERROR: Lima SSH config for ${instance} was not found. Start it first with scripts/dev.sh up." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
port="$(awk 'tolower($1) == "port" { print $2; exit }' "$HOME/.lima/${instance}/ssh.config")"
|
||||
|
||||
if [ -z "$port" ]; then
|
||||
echo "ERROR: Lima SSH port for ${instance} was not found in $HOME/.lima/${instance}/ssh.config." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$port"
|
||||
coold_vm_dns_name "$index"
|
||||
}
|
||||
|
||||
coolify_nodes_arg() {
|
||||
@@ -240,18 +191,37 @@ coolify_wg_endpoint_overrides_arg() {
|
||||
if [ -n "$overrides" ]; then
|
||||
overrides="${overrides},"
|
||||
fi
|
||||
overrides="${overrides}${node}=host.lima.internal:$(coold_vm_wg_port "$index")"
|
||||
overrides="${overrides}${node}=$(coold_vm_dns_name "$index"):$(coold_vm_wg_port "$index")"
|
||||
done
|
||||
|
||||
printf '%s\n' "$overrides"
|
||||
}
|
||||
|
||||
coolify_ssh_key() {
|
||||
read_coolify_env COOLIFY_CLI_SSH_KEY "$HOME/.lima/_config/user"
|
||||
read_coolify_env COOLIFY_CLI_SSH_KEY "/var/www/html/.dev/lima/ssh_key"
|
||||
}
|
||||
|
||||
coolify_host_ssh_key_source() {
|
||||
read_coolify_env COOLIFY_CLI_HOST_SSH_KEY "$HOME/.lima/_config/user"
|
||||
}
|
||||
|
||||
ensure_coolify_container_ssh_key() {
|
||||
local source_key
|
||||
local target_key="$ROOT/.dev/lima/ssh_key"
|
||||
|
||||
source_key="$(coolify_host_ssh_key_source)"
|
||||
if [ ! -f "$source_key" ]; then
|
||||
echo "ERROR: SSH key for dev Lima VMs was not found at ${source_key}." >&2
|
||||
echo "Set COOLIFY_CLI_HOST_SSH_KEY to the host-side private key that Lima authorized." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$target_key")"
|
||||
install -m 0600 "$source_key" "$target_key"
|
||||
}
|
||||
|
||||
coolify_ssh_user() {
|
||||
read_coolify_env COOLIFY_CLI_SSH_USER "$USER"
|
||||
read_coolify_env COOLIFY_CLI_SSH_USER coolify
|
||||
}
|
||||
|
||||
coolify_bootstrap_concurrency() {
|
||||
@@ -264,20 +234,17 @@ coolify_bootstrap_ssh_timeout() {
|
||||
|
||||
coolify_bootstrap_command() {
|
||||
local nodes
|
||||
local ssh_config
|
||||
local listen_overrides
|
||||
local endpoint_overrides
|
||||
ensure_coolify
|
||||
|
||||
nodes="$(coolify_nodes_arg)" || return 1
|
||||
ssh_config="$(lima_ssh_config)" || return 1
|
||||
listen_overrides="$(coolify_wg_listen_overrides_arg)" || return 1
|
||||
endpoint_overrides="$(coolify_wg_endpoint_overrides_arg)" || return 1
|
||||
|
||||
cat <<CMD
|
||||
$(coolify_cli_bin) init bootstrap \\
|
||||
spin exec -T coolify $(coolify_cli_bin) init bootstrap \\
|
||||
--nodes "${nodes}" \\
|
||||
--ssh-config "${ssh_config}" \\
|
||||
--ssh-key "$(coolify_ssh_key)" \\
|
||||
--ssh-user "$(coolify_ssh_user)" \\
|
||||
--concurrency "$(coolify_bootstrap_concurrency)" \\
|
||||
--ssh-timeout "$(coolify_bootstrap_ssh_timeout)" \\
|
||||
@@ -292,19 +259,17 @@ CMD
|
||||
|
||||
coolify_bootstrap() {
|
||||
local nodes
|
||||
local ssh_config
|
||||
local listen_overrides
|
||||
local endpoint_overrides
|
||||
ensure_coolify
|
||||
ensure_coolify_container_ssh_key
|
||||
|
||||
nodes="$(coolify_nodes_arg)" || return 1
|
||||
ssh_config="$(lima_ssh_config)" || return 1
|
||||
listen_overrides="$(coolify_wg_listen_overrides_arg)" || return 1
|
||||
endpoint_overrides="$(coolify_wg_endpoint_overrides_arg)" || return 1
|
||||
|
||||
"$(coolify_cli_bin)" init bootstrap \
|
||||
spin exec -T coolify "$(coolify_cli_bin)" init bootstrap \
|
||||
--nodes "$nodes" \
|
||||
--ssh-config "$ssh_config" \
|
||||
--ssh-key "$(coolify_ssh_key)" \
|
||||
--ssh-user "$(coolify_ssh_user)" \
|
||||
--concurrency "$(coolify_bootstrap_concurrency)" \
|
||||
--ssh-timeout "$(coolify_bootstrap_ssh_timeout)" \
|
||||
@@ -373,27 +338,25 @@ coolify_dev() {
|
||||
|
||||
case "$command" in
|
||||
install)
|
||||
ensure_coolify
|
||||
"$(coolify_cli_bin)" --version
|
||||
echo "==> coolify CLI is provided by the Coolify dev container."
|
||||
spin exec -T coolify "$(coolify_cli_bin)" --version
|
||||
;;
|
||||
path)
|
||||
ensure_coolify
|
||||
coolify_cli_bin
|
||||
;;
|
||||
bootstrap-command)
|
||||
coolify_bootstrap_command
|
||||
;;
|
||||
run)
|
||||
ensure_coolify
|
||||
exec "$(coolify_cli_bin)" "$@"
|
||||
exec spin exec -T coolify "$(coolify_cli_bin)" "$@"
|
||||
;;
|
||||
-h|--help|help)
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/dev.sh coolify <command>
|
||||
|
||||
Commands:
|
||||
install Download/install the nightly coolify dev binary
|
||||
path Print the local coolify path
|
||||
install Print the coolify CLI version from the dev container
|
||||
path Print the coolify CLI path inside the dev container
|
||||
bootstrap-command Print the dev Lima bootstrap command without running it
|
||||
run <args> Run coolify with arbitrary args
|
||||
|
||||
@@ -414,7 +377,6 @@ coold_vm() {
|
||||
shift
|
||||
COOLIFY_COOLD_LIMA_INSTANCE="$(coold_vm_instance "$index")" \
|
||||
COOLIFY_COOLD_VM_WG_IP="$(coold_vm_wg_ip "$index")" \
|
||||
COOLIFY_COOLD_VM_SSH_PORT="$(coold_vm_ssh_port "$index")" \
|
||||
COOLIFY_COOLD_VM_CONTAINER_SUBNET="$(coold_vm_container_subnet "$index")" \
|
||||
COOLIFY_COOLD_VM_CONTAINER_GATEWAY="$(coold_vm_container_gateway "$index")" \
|
||||
scripts/coold-vm.sh "$@"
|
||||
@@ -524,8 +486,7 @@ sync_v5_dev_lima_servers() {
|
||||
|
||||
for index in $(seq 1 "$count"); do
|
||||
instance="$(coold_vm_instance "$index")"
|
||||
ssh_port="$(lima_ssh_port "$index")"
|
||||
server_args+=(--server="${instance}|host.docker.internal|${ssh_user}|${ssh_port}|$(coold_vm_wg_ip "$index")")
|
||||
server_args+=(--server="${instance}|$(coold_vm_dns_name "$index")|${ssh_user}|22|$(coold_vm_wg_ip "$index")")
|
||||
done
|
||||
|
||||
echo "==> Running pending migrations before syncing v5 dev Lima state..."
|
||||
@@ -605,6 +566,10 @@ up() {
|
||||
COOLIFY_CLI_SSH_USER="$(coolify_ssh_user)" spin up -d
|
||||
fi
|
||||
|
||||
if [ "$coold_vm_enabled" != "false" ]; then
|
||||
sync_lima_hosts_into_coolify_container
|
||||
fi
|
||||
|
||||
if [ "$naked" = "true" ]; then
|
||||
echo "==> --naked enabled. Skipping coolify bootstrap and Flux VM wiring. Use /v5 to bootstrap hosts from the UI."
|
||||
return
|
||||
@@ -940,7 +905,7 @@ SH
|
||||
|
||||
example_nginx_require_pair() {
|
||||
if [ "$(coold_vm_count)" = "1" ]; then
|
||||
echo "ERROR: example-nginx ping/firewall commands require COOLIFY_COOLD_VM_COUNT=2." >&2
|
||||
echo "ERROR: example-nginx ping command requires COOLIFY_COOLD_VM_COUNT=2." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -966,28 +931,6 @@ echo 'ok: coolify-example-nginx can reach coolify-example-nginx-2 on tcp/80'
|
||||
SH
|
||||
}
|
||||
|
||||
example_nginx_firewall_up() {
|
||||
local src
|
||||
local dst
|
||||
example_nginx_require_pair
|
||||
|
||||
src="$(example_nginx_container_ip 1)"
|
||||
dst="$(example_nginx_container_ip 2)"
|
||||
|
||||
scripts/dev.sh firewall allow "$src" "$dst" tcp 80
|
||||
}
|
||||
|
||||
example_nginx_firewall_down() {
|
||||
local src
|
||||
local dst
|
||||
example_nginx_require_pair
|
||||
|
||||
src="$(example_nginx_container_ip 1)"
|
||||
dst="$(example_nginx_container_ip 2)"
|
||||
|
||||
scripts/dev.sh firewall revoke "$src" "$dst" tcp 80
|
||||
}
|
||||
|
||||
example_nginx_help() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/dev.sh example-nginx <command>
|
||||
@@ -997,8 +940,6 @@ Commands:
|
||||
down Remove the example nginx containers
|
||||
check-dns Verify host 1 nginx can resolve host 2 nginx through coold DNS
|
||||
ping Verify host 1 nginx can reach host 2 nginx on tcp/80
|
||||
firewall up Allow host 1 nginx to reach host 2 nginx through the coolify CLI
|
||||
firewall down Revoke the example nginx tcp/80 allow rule through the coolify CLI
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -1021,21 +962,6 @@ example_nginx() {
|
||||
ping)
|
||||
example_nginx_ping
|
||||
;;
|
||||
firewall)
|
||||
case "${1:-help}" in
|
||||
up)
|
||||
example_nginx_firewall_up
|
||||
;;
|
||||
down)
|
||||
example_nginx_firewall_down
|
||||
;;
|
||||
*)
|
||||
echo "unknown example-nginx firewall command: ${1:-help}" >&2
|
||||
echo "Run: scripts/dev.sh example-nginx help" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-h|--help|help)
|
||||
example_nginx_help
|
||||
;;
|
||||
@@ -1047,139 +973,6 @@ example_nginx() {
|
||||
esac
|
||||
}
|
||||
|
||||
firewall_help() {
|
||||
cat <<'USAGE'
|
||||
Usage: scripts/dev.sh firewall <command>
|
||||
|
||||
Commands:
|
||||
allow <src> <dst> [proto] [port] Allow traffic through the coolify CLI (proto/port optional)
|
||||
revoke [id|src] [dst] [proto] [port]
|
||||
Remove an allow rule through the coolify CLI
|
||||
list List allow rules through the coolify CLI
|
||||
containers List registered containers through the coolify CLI
|
||||
|
||||
Examples:
|
||||
scripts/dev.sh firewall allow 10.210.0.2 10.210.1.2 tcp 80
|
||||
scripts/dev.sh firewall revoke
|
||||
scripts/dev.sh firewall revoke 10.210.0.2 10.210.1.2 tcp 80
|
||||
scripts/dev.sh firewall revoke 3ba6e0c235a6
|
||||
scripts/dev.sh firewall list
|
||||
USAGE
|
||||
}
|
||||
|
||||
coolify_firewall() {
|
||||
local command="$1"
|
||||
shift
|
||||
local nodes
|
||||
local ssh_config
|
||||
|
||||
ensure_coolify
|
||||
nodes="$(coolify_nodes_arg)" || return 1
|
||||
ssh_config="$(lima_ssh_config)" || return 1
|
||||
|
||||
"$(coolify_cli_bin)" firewall "$command" \
|
||||
--nodes "$nodes" \
|
||||
--ssh-config "$ssh_config" \
|
||||
--ssh-user "$(coolify_ssh_user)" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
firewall_allow() {
|
||||
local src="${1:-}"
|
||||
local dst="${2:-}"
|
||||
local proto="${3:-}"
|
||||
local port="${4:-}"
|
||||
local args=()
|
||||
|
||||
if [ -z "$src" ] || [ -z "$dst" ]; then
|
||||
firewall_help >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$port" ] && [ -z "$proto" ]; then
|
||||
echo "ERROR: port requires proto (tcp or udp)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
args+=(--from "$src" --to "$dst")
|
||||
if [ -n "$proto" ]; then
|
||||
args+=(--proto "$proto")
|
||||
fi
|
||||
if [ -n "$port" ]; then
|
||||
args+=(--port "$port")
|
||||
fi
|
||||
|
||||
coolify_firewall allow "${args[@]}"
|
||||
}
|
||||
|
||||
firewall_revoke() {
|
||||
local id_or_src="${1:-}"
|
||||
local dst="${2:-}"
|
||||
local proto="${3:-}"
|
||||
local port="${4:-}"
|
||||
local args=()
|
||||
|
||||
if [ -z "$id_or_src" ]; then
|
||||
echo "Current firewall allow rule IDs:"
|
||||
firewall_list
|
||||
echo
|
||||
echo "Revoke one with: scripts/dev.sh firewall revoke <id>"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ -z "$dst" ]; then
|
||||
args+=(--id "$id_or_src")
|
||||
else
|
||||
args+=(--from "$id_or_src" --to "$dst")
|
||||
if [ -n "$proto" ]; then
|
||||
args+=(--proto "$proto")
|
||||
fi
|
||||
if [ -n "$port" ]; then
|
||||
args+=(--port "$port")
|
||||
fi
|
||||
fi
|
||||
|
||||
coolify_firewall revoke "${args[@]}"
|
||||
}
|
||||
|
||||
firewall_list() {
|
||||
coolify_firewall list "$@"
|
||||
}
|
||||
|
||||
firewall_containers() {
|
||||
coolify_firewall containers "$@"
|
||||
}
|
||||
|
||||
firewall() {
|
||||
local command="${1:-help}"
|
||||
if [ $# -gt 0 ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case "$command" in
|
||||
allow)
|
||||
firewall_allow "$@"
|
||||
;;
|
||||
revoke|remove|delete|deny)
|
||||
firewall_revoke "$@"
|
||||
;;
|
||||
list)
|
||||
firewall_list "$@"
|
||||
;;
|
||||
containers)
|
||||
firewall_containers "$@"
|
||||
;;
|
||||
-h|--help|help)
|
||||
firewall_help
|
||||
;;
|
||||
*)
|
||||
echo "unknown firewall command: $command" >&2
|
||||
echo "Run: scripts/dev.sh firewall help" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
refresh_test_host_key() {
|
||||
echo "==> Refreshing /tmp/testhostkey inside coolify..."
|
||||
@@ -1258,7 +1051,6 @@ Commands:
|
||||
clean-vms Delete the coold Lima VMs and all VM-local runtime state (alias for down --cleanup)
|
||||
naked-vm Recreate the naked Lima VM used for bootstrap testing
|
||||
corrosion <command> Inspect Corrosion state, config, logs, and registered containers
|
||||
firewall <command> Manage dev coold firewall allow rules
|
||||
example-nginx <command> Start/check example nginx containers with coold DNS
|
||||
coolify <command> Install/run the released coolify dev helper
|
||||
USAGE
|
||||
@@ -1294,9 +1086,6 @@ case "$cmd" in
|
||||
corrosion)
|
||||
corrosion "$@"
|
||||
;;
|
||||
firewall)
|
||||
firewall "$@"
|
||||
;;
|
||||
example-nginx)
|
||||
example_nginx "$@"
|
||||
;;
|
||||
|
||||
@@ -21,11 +21,10 @@ it('does not include coold dev tooling defaults in the development env example',
|
||||
]);
|
||||
|
||||
it('defaults coold dev VM settings in Laravel config', function () {
|
||||
expect(config('coold.dev_host_count'))->toBe(2)
|
||||
->and(config('coold.dev_host_id'))->toBe('coolify-coold-dev')
|
||||
->and(config('coold.dev_host_id_2'))->toBe('coolify-coold-dev-2')
|
||||
->and(config('coold.dev_wireguard_ip_1'))->toBe('100.64.0.10')
|
||||
->and(config('coold.dev_wireguard_ip_2'))->toBe('100.64.0.11')
|
||||
expect(config('coold.coolify_cli_bin'))->toBe('/usr/local/bin/coolify')
|
||||
->and(config('coold.coold_version'))->toBe('nightly')
|
||||
->and(config('coold.corrosion_version'))->toBe('v1.0.0')
|
||||
->and(config('coold.dev_ssh_user'))->toBe('coolify')
|
||||
->and(config('coold.dev_builder_capacity'))->toBe(2)
|
||||
->and(config('coold.dev_builder_enabled'))->toBeTrue();
|
||||
});
|
||||
|
||||
@@ -2,22 +2,22 @@
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
it('documents example nginx connectivity and firewall commands', function () {
|
||||
it('documents example nginx connectivity commands', function () {
|
||||
$process = new Process(['bash', base_path('scripts/dev.sh'), 'example-nginx', 'help'], base_path());
|
||||
|
||||
$process->run();
|
||||
|
||||
expect($process->isSuccessful())->toBeTrue()
|
||||
->and($process->getOutput())->toContain('ping')
|
||||
->and($process->getOutput())->toContain('firewall up')
|
||||
->and($process->getOutput())->toContain('firewall down')
|
||||
->and($process->getOutput())->not->toContain('firewall up')
|
||||
->and($process->getOutput())->not->toContain('firewall down')
|
||||
->and($process->getOutput())->not->toContain('firewall-up')
|
||||
->and($process->getOutput())->not->toContain('firewall-down');
|
||||
});
|
||||
|
||||
it('uses the coolify firewall wrapper for example nginx firewall changes', function () {
|
||||
it('does not wire example nginx to removed firewall CLI commands', function () {
|
||||
$script = file_get_contents(base_path('scripts/dev.sh'));
|
||||
|
||||
expect($script)->toContain('scripts/dev.sh firewall allow "$src" "$dst" tcp 80')
|
||||
->and($script)->toContain('scripts/dev.sh firewall revoke "$src" "$dst" tcp 80');
|
||||
expect($script)->not->toContain('scripts/dev.sh firewall allow "$src" "$dst" tcp 80')
|
||||
->and($script)->not->toContain('scripts/dev.sh firewall revoke "$src" "$dst" tcp 80');
|
||||
});
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
|
||||
it('delegates dev firewall commands to the coolify CLI instead of calling coold APIs directly', function () {
|
||||
it('does not expose removed dev firewall commands', function () {
|
||||
$script = file_get_contents(base_path('scripts/dev.sh'));
|
||||
|
||||
expect($script)->toContain('coolify_firewall()')
|
||||
->and($script)->toContain('"$(coolify_cli_bin)" firewall "$command"')
|
||||
->and($script)->toContain('--nodes "$nodes"')
|
||||
->and($script)->toContain('--ssh-config "$ssh_config"')
|
||||
expect($script)->not->toContain('coolify_firewall()')
|
||||
->and($script)->not->toContain('scripts/dev.sh firewall')
|
||||
->and($script)->not->toContain('firewall <command>')
|
||||
->and($script)->not->toContain('/api/v1/firewall/allow')
|
||||
->and($script)->not->toContain('firewall_api_for_each_vm');
|
||||
});
|
||||
@@ -22,13 +21,28 @@ it('installs the coolify CLI in both application container images', function (st
|
||||
'production image' => 'docker/production/Dockerfile',
|
||||
]);
|
||||
|
||||
it('refreshes the host coolify CLI on every dev script run', function () {
|
||||
it('runs the coolify CLI from the development application container', function () {
|
||||
$script = file_get_contents(base_path('scripts/dev.sh'));
|
||||
|
||||
expect($script)->toContain('url="https://github.com/coollabsio/coold/releases/download/${version}/coolify-${os}-${arch}.tar.gz"')
|
||||
->and($script)->toContain('==> Installing coolify from ${url}')
|
||||
->and($script)->not->toContain('COOLIFY_CLI_FORCE_DOWNLOAD')
|
||||
->and($script)->not->toContain('if [ -x "$bin" ] && "$bin" --version >/dev/null 2>&1');
|
||||
expect($script)->toContain("printf '%s\\n' '/usr/local/bin/coolify'")
|
||||
->and($script)->toContain('spin exec -T coolify "$(coolify_cli_bin)" init bootstrap')
|
||||
->and($script)->toContain('spin exec -T coolify "$(coolify_cli_bin)" "$@"')
|
||||
->and($script)->toContain('ensure_coolify_container_ssh_key')
|
||||
->and($script)->not->toContain('.dev/bin/coolify')
|
||||
->and($script)->not->toContain('coolify-${os}-${arch}.tar.gz');
|
||||
});
|
||||
|
||||
it('syncs host-resolved Lima local names into the Coolify container hosts file', function () {
|
||||
$script = file_get_contents(base_path('scripts/dev.sh'));
|
||||
|
||||
expect($script)->toContain('resolve_lima_dns_name()')
|
||||
->and($script)->toContain('dscacheutil -q host -a name "$name"')
|
||||
->and($script)->toContain('getent ahostsv4 "$name"')
|
||||
->and($script)->toContain('sync_lima_hosts_into_coolify_container()')
|
||||
->and($script)->toContain('spin exec -T -u root coolify sh -lc')
|
||||
->and($script)->toContain('cat "$next" > /etc/hosts')
|
||||
->and($script)->toContain('sync_lima_hosts_into_coolify_container')
|
||||
->and($script)->toContain('if [ "$naked" = "true" ]; then');
|
||||
});
|
||||
|
||||
it('does not require predefined UI node environment variables in the development app container', function () {
|
||||
@@ -81,28 +95,38 @@ it('seeds bootstrapped Lima VMs into v5 development server state', function () {
|
||||
expect($script)->toContain('sync_v5_dev_lima_servers()')
|
||||
->and($script)->toContain('COOLIFY_CLI_SSH_USER="$ssh_user"')
|
||||
->and($script)->toContain('COOLIFY_CLI_SSH_USER="$(coolify_ssh_user)" spin up -d')
|
||||
->and($script)->toContain('lima_ssh_port "$index"')
|
||||
->and($script)->toContain('host.docker.internal')
|
||||
->and($script)->toContain('coold_vm_dns_name()')
|
||||
->and($script)->toContain('$(coold_vm_dns_name "$index")')
|
||||
->and($script)->toContain('v5:sync-dev-lima-servers')
|
||||
->and($script)->toContain('--server="${instance}|host.docker.internal|${ssh_user}|${ssh_port}|$(coold_vm_wg_ip "$index")"')
|
||||
->and($script)->toContain('--server="${instance}|$(coold_vm_dns_name "$index")|${ssh_user}|22|$(coold_vm_wg_ip "$index")"')
|
||||
->and($compose)->toContain('COOLIFY_CLI_SSH_USER: "${COOLIFY_CLI_SSH_USER:-}"')
|
||||
->and($script)->not->toContain('db:seed --class=V5DevLimaSeeder --force')
|
||||
->and($script)->not->toContain('--server "${instance}|${node}|$(coolify_ssh_user)|22"');
|
||||
->and($script)->not->toContain('host.docker.internal|${ssh_user}');
|
||||
});
|
||||
|
||||
it('configures predictable Lima SSH local ports for dev VMs', function () {
|
||||
it('uses the shared Lima template directly instead of generated per-VM YAML', function () {
|
||||
$script = file_get_contents(base_path('scripts/dev.sh'));
|
||||
$vmScript = file_get_contents(base_path('scripts/coold-vm.sh'));
|
||||
$template = file_get_contents(base_path('dev/lima/coold.yaml'));
|
||||
|
||||
expect($script)->toContain('coold_vm_ssh_port()')
|
||||
->and($script)->toContain('COOLIFY_COOLD_VM_SSH_PORT_')
|
||||
->and($script)->toContain('6000${index}')
|
||||
->and($script)->toContain('COOLIFY_COOLD_VM_SSH_PORT="$(coold_vm_ssh_port "$index")"')
|
||||
->and($vmScript)->toContain('SSH_PORT="$(read_coolify_env COOLIFY_COOLD_VM_SSH_PORT 60002)"')
|
||||
->and($vmScript)->toContain('{{COOLIFY_COOLD_VM_SSH_PORT}}')
|
||||
->and($template)->toContain('ssh:')
|
||||
->and($template)->toContain('localPort: {{COOLIFY_COOLD_VM_SSH_PORT}}');
|
||||
expect($script)->toContain('coold_vm_dns_name()')
|
||||
->and($script)->not->toContain('COOLIFY_COOLD_VM_SSH_PORT="$(coold_vm_ssh_port "$index")"')
|
||||
->and($vmScript)->toContain('limactl start --tty=false --name="$INSTANCE" "$TEMPLATE"')
|
||||
->and($vmScript)->not->toContain('GENERATED=')
|
||||
->and($vmScript)->not->toContain('generate_yaml()')
|
||||
->and($template)->not->toContain('{{COOLIFY_COOLD_VM_SSH_PORT}}');
|
||||
});
|
||||
|
||||
it('sets the Lima guest hostname to the instance name for predictable mDNS', function () {
|
||||
$vmScript = file_get_contents(base_path('scripts/coold-vm.sh'));
|
||||
$template = file_get_contents(base_path('dev/lima/coold.yaml'));
|
||||
|
||||
expect($vmScript)->toContain('ensure_mdns_hostname()')
|
||||
->and($vmScript)->toContain('hostnamectl set-hostname "$INSTANCE"')
|
||||
->and($vmScript)->toContain('systemctl restart avahi-daemon.service')
|
||||
->and($vmScript)->toContain('ensure_mdns_hostname')
|
||||
->and($template)->toContain('hostnamectl set-hostname "{{.Name}}"')
|
||||
->and($template)->not->toContain('hostnamectl set-hostname myvm');
|
||||
});
|
||||
|
||||
it('authorizes the seeded testing host key in dev Lima VMs', function () {
|
||||
@@ -112,10 +136,11 @@ it('authorizes the seeded testing host key in dev Lima VMs', function () {
|
||||
->and($template)->toContain('ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFuGmoeGq/pojrsyP1pszcNVuZx9iFkCELtxrh31QJ68')
|
||||
->and($template)->toContain('authorized_keys')
|
||||
->and($template)->toContain('grep -qxF "$coolify_test_public_key"')
|
||||
->and($template)->toContain('install -d -m 700 /root/.ssh')
|
||||
->and($template)->toContain('/root/.ssh/authorized_keys')
|
||||
->and($template)->toContain('target_home="$(getent passwd 501 | cut -d: -f6 || true)"')
|
||||
->and($template)->toContain('owner="$(stat -c \'%u:%g\' "$target_home")"')
|
||||
->and($template)->toContain('install_public_key root')
|
||||
->and($template)->toContain('install_public_key coolify')
|
||||
->and($template)->toContain('owner="$(stat -c \'%u:%g\' "$home")"')
|
||||
->and($template)->toContain('hostnamectl set-hostname "{{.Name}}"')
|
||||
->and($template)->not->toContain('hostnamectl set-hostname myvm')
|
||||
->and($template)->not->toContain('mode: user')
|
||||
->and($template)->not->toContain('user="$(basename "$home")"');
|
||||
});
|
||||
|
||||
@@ -4710,8 +4710,8 @@ it('seeds dev Lima VMs into v5 clusters and servers idempotently', function () {
|
||||
->and($cluster->created_by_user_id)->toBe($user->id)
|
||||
->and($cluster->description)->toBe('Local Lima development cluster managed by scripts/dev.sh.')
|
||||
->and(V5Server::query()->count())->toBe(2)
|
||||
->and(V5Server::query()->where('name', 'coold-dev')->where('host', 'host.docker.internal')->where('ssh_user', get_current_user())->where('ssh_port', 60001)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev-2')->where('host', 'host.docker.internal')->where('ssh_user', get_current_user())->where('ssh_port', 60002)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev')->where('host', 'coold-dev.local')->where('ssh_user', 'coolify')->where('ssh_port', 22)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev-2')->where('host', 'coold-dev-2.local')->where('ssh_user', 'coolify')->where('ssh_port', 22)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev')->where('node_address', '100.64.0.1')->where('wireguard_management_ip', '100.64.0.1')->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev-2')->where('node_address', '100.64.0.2')->where('wireguard_management_ip', '100.64.0.2')->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('status', 'installed')->count())->toBe(2)
|
||||
@@ -4747,7 +4747,7 @@ it('seeds dev Lima VMs by updating existing named servers', function () {
|
||||
(new V5DevLimaSeeder)->run();
|
||||
|
||||
expect(V5Server::query()->where('name', 'coold-dev')->count())->toBe(1)
|
||||
->and(V5Server::query()->where('name', 'coold-dev')->where('host', 'host.docker.internal')->where('ssh_port', 60001)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->where('name', 'coold-dev')->where('host', 'coold-dev.local')->where('ssh_port', 22)->exists())->toBeTrue()
|
||||
->and(V5Server::query()->count())->toBe(2);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user