diff --git a/installer/CLAUDE.md b/installer/CLAUDE.md index 4249b78..3c9a2af 100644 --- a/installer/CLAUDE.md +++ b/installer/CLAUDE.md @@ -31,6 +31,14 @@ Env vars: `SMTP_HOST`, `SMTP_PORT` (default 587), `SMTP_USER`, `SMTP_PASS`, `SMT CLI args: `--smtp-host`, `--smtp-port`, `--smtp-user`, `--smtp-pass`, `--smtp-from-email` (bash) / `-SmtpHost`, `-SmtpPort`, `-SmtpUser`, `-SmtpPass`, `-SmtpFromEmail` (PS1). Persisted in `cameleer.conf` for upgrades/reconfigure. +## Registry configuration + +Both installers support pulling images from a custom Docker registry via `--registry` (bash) / `-Registry` (PS1). Default: `gitea.siegeln.net/cameleer`. + +When a registry is configured, the installer writes `*_IMAGE` env vars to `.env` (e.g. `TRAEFIK_IMAGE`, `POSTGRES_IMAGE`, `CAMELEER_IMAGE`) which override the defaults baked into the compose templates. In SaaS mode, provisioning image refs (`CAMELEER_SAAS_PROVISIONING_*IMAGE`) are also set from the registry. + +For private registries, pass `--registry-user` / `--registry-token` (bash) or `-RegistryUser` / `-RegistryToken` (PS1). The installer runs `docker login` before pulling images. Credentials are persisted in `cameleer.conf` for upgrades/reconfigure. + ## Env var naming convention - `CAMELEER_AGENT_*` — agent config (consumed by the Java agent) diff --git a/installer/install.ps1 b/installer/install.ps1 index 686afe0..2ba2db3 100644 --- a/installer/install.ps1 +++ b/installer/install.ps1 @@ -42,6 +42,9 @@ param( [string]$SmtpUser, [string]$SmtpPass, [string]$SmtpFromEmail, + [string]$Registry, + [string]$RegistryUser, + [string]$RegistryToken, [switch]$Reconfigure, [switch]$Reinstall, [switch]$ConfirmDestroy, @@ -55,7 +58,7 @@ $ErrorActionPreference = 'Stop' $CAMELEER_INSTALLER_VERSION = '1.0.0' $CAMELEER_DEFAULT_VERSION = 'latest' -$REGISTRY = 'gitea.siegeln.net/cameleer' +$DEFAULT_REGISTRY = 'gitea.siegeln.net/cameleer' $DEFAULT_INSTALL_DIR = './cameleer' $DEFAULT_PUBLIC_PROTOCOL = 'https' @@ -94,6 +97,9 @@ $_ENV_SMTP_PORT = $env:SMTP_PORT $_ENV_SMTP_USER = $env:SMTP_USER $_ENV_SMTP_PASS = $env:SMTP_PASS $_ENV_SMTP_FROM_EMAIL = $env:SMTP_FROM_EMAIL +$_ENV_REGISTRY = $env:REGISTRY +$_ENV_REGISTRY_USER = $env:REGISTRY_USER +$_ENV_REGISTRY_TOKEN = $env:REGISTRY_TOKEN # --- Mutable config state --- @@ -125,6 +131,9 @@ $script:cfg = @{ SmtpUser = $SmtpUser SmtpPass = $SmtpPass SmtpFromEmail = $SmtpFromEmail + Registry = $Registry + RegistryUser = $RegistryUser + RegistryToken = $RegistryToken } if ($Silent) { $script:Mode = 'silent' } @@ -180,6 +189,11 @@ function Show-Help { Write-Host ' -Config FILE Load config from file' Write-Host ' -Help Show this help' Write-Host '' + Write-Host 'Registry options:' + Write-Host ' -Registry REGISTRY Image registry (default: gitea.siegeln.net/cameleer)' + Write-Host ' -RegistryUser USER Registry username for docker login' + Write-Host ' -RegistryToken TOKEN Registry token/password for docker login' + Write-Host '' Write-Host 'Expert options:' Write-Host ' -PostgresPassword, -ClickhousePassword, -HttpPort,' Write-Host ' -HttpsPort, -LogtoConsolePort, -LogtoConsoleExposed,' @@ -280,6 +294,9 @@ function Load-ConfigFile { 'smtp_user' { if (-not $script:cfg.SmtpUser) { $script:cfg.SmtpUser = $val } } 'smtp_pass' { if (-not $script:cfg.SmtpPass) { $script:cfg.SmtpPass = $val } } 'smtp_from_email' { if (-not $script:cfg.SmtpFromEmail) { $script:cfg.SmtpFromEmail = $val } } + 'registry' { if (-not $script:cfg.Registry) { $script:cfg.Registry = $val } } + 'registry_user' { if (-not $script:cfg.RegistryUser) { $script:cfg.RegistryUser = $val } } + 'registry_token' { if (-not $script:cfg.RegistryToken) { $script:cfg.RegistryToken = $val } } } } } @@ -314,6 +331,9 @@ function Load-EnvOverrides { if (-not $c.SmtpUser) { $c.SmtpUser = $_ENV_SMTP_USER } if (-not $c.SmtpPass) { $c.SmtpPass = $_ENV_SMTP_PASS } if (-not $c.SmtpFromEmail) { $c.SmtpFromEmail = $_ENV_SMTP_FROM_EMAIL } + if (-not $c.Registry) { $c.Registry = $_ENV_REGISTRY } + if (-not $c.RegistryUser) { $c.RegistryUser = $_ENV_REGISTRY_USER } + if (-not $c.RegistryToken) { $c.RegistryToken = $_ENV_REGISTRY_TOKEN } } # --- Prerequisites --- @@ -542,6 +562,7 @@ function Merge-Config { if (-not $c.LogtoConsoleExposed) { $c.LogtoConsoleExposed = $DEFAULT_LOGTO_CONSOLE_EXPOSED } if (-not $c.Version) { $c.Version = $CAMELEER_DEFAULT_VERSION } if (-not $c.DockerSocket) { $c.DockerSocket = $DEFAULT_DOCKER_SOCKET } + if (-not $c.Registry) { $c.Registry = $DEFAULT_REGISTRY } if (-not $c.ComposeProject) { if ($c.DeploymentMode -eq 'standalone') { @@ -662,6 +683,12 @@ DOCKER_SOCKET=$($c.DockerSocket) DOCKER_GID=$gid POSTGRES_IMAGE=postgres:16-alpine + +# Registry +TRAEFIK_IMAGE=$($c.Registry)/cameleer-traefik +CLICKHOUSE_IMAGE=$($c.Registry)/cameleer-clickhouse +SERVER_IMAGE=$($c.Registry)/cameleer-server +SERVER_UI_IMAGE=$($c.Registry)/cameleer-server-ui "@ if ($c.TlsMode -eq 'custom') { $content += "`nCERT_FILE=/user-certs/cert.pem" @@ -712,16 +739,24 @@ NODE_TLS_REJECT=$($c.NodeTlsReject) $content += "`nKEY_FILE=/user-certs/key.pem" if ($c.CaFile) { $content += "`nCA_FILE=/user-certs/ca.pem" } } + $reg = $c.Registry $provisioningBlock = @" - + # Docker DOCKER_SOCKET=$($c.DockerSocket) DOCKER_GID=$gid - + +# Registry +TRAEFIK_IMAGE=$reg/cameleer-traefik +POSTGRES_IMAGE=$reg/cameleer-postgres +CLICKHOUSE_IMAGE=$reg/cameleer-clickhouse +LOGTO_IMAGE=$reg/cameleer-logto +CAMELEER_IMAGE=$reg/cameleer-saas + # Provisioning images -CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:$($c.Version) -CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:$($c.Version) -CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=${REGISTRY}/cameleer-runtime-base:$($c.Version) +CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=$reg/cameleer-server:$($c.Version) +CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=$reg/cameleer-server-ui:$($c.Version) +CAMELEER_SAAS_PROVISIONING_RUNTIMEBASEIMAGE=$reg/cameleer-runtime-base:$($c.Version) # JWT signing secret (forwarded to provisioned tenant servers, must be non-empty) CAMELEER_SERVER_SECURITY_JWTSECRET=$jwtSecret @@ -778,7 +813,18 @@ function Copy-Templates { } # --- Docker operations --- - + +function Invoke-RegistryLogin { + $c = $script:cfg + if ($c.RegistryUser -and $c.RegistryToken) { + $registryHost = $c.Registry.Split('/')[0] + Log-Info "Logging in to registry ${registryHost}..." + $c.RegistryToken | docker login $registryHost -u $c.RegistryUser --password-stdin + if ($LASTEXITCODE -ne 0) { Log-Error 'Registry login failed.'; exit 1 } + Log-Success 'Registry login successful.' + } +} + function Invoke-ComposePull { $c = $script:cfg Log-Info 'Pulling Docker images...' @@ -964,6 +1010,9 @@ smtp_port=$($c.SmtpPort) smtp_user=$($c.SmtpUser) smtp_pass=$($c.SmtpPass) smtp_from_email=$($c.SmtpFromEmail) +registry=$($c.Registry) +registry_user=$($c.RegistryUser) +registry_token=$($c.RegistryToken) "@ Write-Utf8File $f $txt Log-Info 'Saved installer config to cameleer.conf' @@ -1393,6 +1442,7 @@ function Handle-Rerun { $script:cfg.InstallDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath( $script:cfg.InstallDir) Copy-Templates + Invoke-RegistryLogin Invoke-ComposePull Invoke-ComposeDown Invoke-ComposeUp @@ -1477,10 +1527,11 @@ function Main { Copy-Templates Write-ConfigFile + Invoke-RegistryLogin Invoke-ComposePull Invoke-ComposeUp Verify-Health - + Generate-CredentialsFile Generate-InstallDoc diff --git a/installer/install.sh b/installer/install.sh index 030b2a8..f32b4d2 100644 --- a/installer/install.sh +++ b/installer/install.sh @@ -3,7 +3,7 @@ set -euo pipefail CAMELEER_INSTALLER_VERSION="1.0.0" CAMELEER_DEFAULT_VERSION="latest" -REGISTRY="gitea.siegeln.net/cameleer" +DEFAULT_REGISTRY="gitea.siegeln.net/cameleer" # --- Colors --- RED='\033[0;31m' @@ -51,6 +51,9 @@ _ENV_SMTP_PORT="${SMTP_PORT:-}" _ENV_SMTP_USER="${SMTP_USER:-}" _ENV_SMTP_PASS="${SMTP_PASS:-}" _ENV_SMTP_FROM_EMAIL="${SMTP_FROM_EMAIL:-}" +_ENV_REGISTRY="${REGISTRY:-}" +_ENV_REGISTRY_USER="${REGISTRY_USER:-}" +_ENV_REGISTRY_TOKEN="${REGISTRY_TOKEN:-}" INSTALL_DIR="" PUBLIC_HOST="" @@ -79,6 +82,9 @@ SMTP_PORT="" SMTP_USER="" SMTP_PASS="" SMTP_FROM_EMAIL="" +REGISTRY="" +REGISTRY_USER="" +REGISTRY_TOKEN="" # --- State --- MODE="" # simple, expert, silent @@ -185,6 +191,9 @@ parse_args() { --smtp-user) SMTP_USER="$2"; shift ;; --smtp-pass) SMTP_PASS="$2"; shift ;; --smtp-from-email) SMTP_FROM_EMAIL="$2"; shift ;; + --registry) REGISTRY="$2"; shift ;; + --registry-user) REGISTRY_USER="$2"; shift ;; + --registry-token) REGISTRY_TOKEN="$2"; shift ;; --server-admin-user) ADMIN_USER="$2"; shift ;; --server-admin-password) ADMIN_PASS="$2"; shift ;; --reconfigure) RERUN_ACTION="reconfigure" ;; @@ -224,6 +233,11 @@ show_help() { echo " --config FILE Load config from file" echo " --help Show this help" echo "" + echo "Registry options:" + echo " --registry REGISTRY Image registry (default: gitea.siegeln.net/cameleer)" + echo " --registry-user USER Registry username for docker login" + echo " --registry-token TOKEN Registry token/password for docker login" + echo "" echo "Expert options:" echo " --postgres-password, --clickhouse-password, --http-port," echo " --https-port, --logto-console-port, --logto-console-exposed," @@ -275,6 +289,9 @@ load_config_file() { smtp_user) [ -z "$SMTP_USER" ] && SMTP_USER="$value" ;; smtp_pass) [ -z "$SMTP_PASS" ] && SMTP_PASS="$value" ;; smtp_from_email) [ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$value" ;; + registry) [ -z "$REGISTRY" ] && REGISTRY="$value" ;; + registry_user) [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$value" ;; + registry_token) [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$value" ;; esac done < "$file" } @@ -307,6 +324,9 @@ load_env_overrides() { [ -z "$SMTP_USER" ] && SMTP_USER="$_ENV_SMTP_USER" [ -z "$SMTP_PASS" ] && SMTP_PASS="$_ENV_SMTP_PASS" [ -z "$SMTP_FROM_EMAIL" ] && SMTP_FROM_EMAIL="$_ENV_SMTP_FROM_EMAIL" + [ -z "$REGISTRY" ] && REGISTRY="$_ENV_REGISTRY" + [ -z "$REGISTRY_USER" ] && REGISTRY_USER="$_ENV_REGISTRY_USER" + [ -z "$REGISTRY_TOKEN" ] && REGISTRY_TOKEN="$_ENV_REGISTRY_TOKEN" } # --- Prerequisites --- @@ -530,6 +550,7 @@ merge_config() { : "${LOGTO_CONSOLE_EXPOSED:=$DEFAULT_LOGTO_CONSOLE_EXPOSED}" : "${VERSION:=$CAMELEER_DEFAULT_VERSION}" : "${DOCKER_SOCKET:=$DEFAULT_DOCKER_SOCKET}" + : "${REGISTRY:=$DEFAULT_REGISTRY}" if [ "$DEPLOYMENT_MODE" = "standalone" ]; then : "${COMPOSE_PROJECT:=$DEFAULT_COMPOSE_PROJECT_STANDALONE}" @@ -657,6 +678,12 @@ DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") POSTGRES_IMAGE=postgres:16-alpine +# Registry +TRAEFIK_IMAGE=${REGISTRY}/cameleer-traefik +CLICKHOUSE_IMAGE=${REGISTRY}/cameleer-clickhouse +SERVER_IMAGE=${REGISTRY}/cameleer-server +SERVER_UI_IMAGE=${REGISTRY}/cameleer-server-ui + # Compose file assembly COMPOSE_FILE=docker-compose.yml:docker-compose.server.yml$([ "$TLS_MODE" = "custom" ] && echo ":docker-compose.tls.yml")$([ -n "$MONITORING_NETWORK" ] && echo ":docker-compose.monitoring.yml") EOF @@ -725,6 +752,13 @@ EOF DOCKER_SOCKET=${DOCKER_SOCKET} DOCKER_GID=$(stat -c '%g' "${DOCKER_SOCKET}" 2>/dev/null || echo "0") +# Registry +TRAEFIK_IMAGE=${REGISTRY}/cameleer-traefik +POSTGRES_IMAGE=${REGISTRY}/cameleer-postgres +CLICKHOUSE_IMAGE=${REGISTRY}/cameleer-clickhouse +LOGTO_IMAGE=${REGISTRY}/cameleer-logto +CAMELEER_IMAGE=${REGISTRY}/cameleer-saas + # Provisioning images CAMELEER_SAAS_PROVISIONING_SERVERIMAGE=${REGISTRY}/cameleer-server:${VERSION} CAMELEER_SAAS_PROVISIONING_SERVERUIIMAGE=${REGISTRY}/cameleer-server-ui:${VERSION} @@ -783,6 +817,16 @@ copy_templates() { # --- Docker operations --- +docker_registry_login() { + if [ -n "$REGISTRY_USER" ] && [ -n "$REGISTRY_TOKEN" ]; then + local registry_host + registry_host=$(echo "$REGISTRY" | cut -d/ -f1) + log_info "Logging in to registry ${registry_host}..." + echo "$REGISTRY_TOKEN" | docker login "$registry_host" -u "$REGISTRY_USER" --password-stdin + log_success "Registry login successful." + fi +} + docker_compose_pull() { log_info "Pulling Docker images..." (cd "$INSTALL_DIR" && docker compose -p "$COMPOSE_PROJECT" pull) @@ -916,6 +960,9 @@ smtp_port=${SMTP_PORT} smtp_user=${SMTP_USER} smtp_pass=${SMTP_PASS} smtp_from_email=${SMTP_FROM_EMAIL} +registry=${REGISTRY} +registry_user=${REGISTRY_USER} +registry_token=${REGISTRY_TOKEN} EOF log_info "Saved installer config to cameleer.conf" } @@ -1354,6 +1401,7 @@ handle_rerun() { load_env_overrides merge_config copy_templates + docker_registry_login docker_compose_pull docker_compose_down docker_compose_up @@ -1448,6 +1496,7 @@ main() { write_config_file # Pull and start + docker_registry_login docker_compose_pull docker_compose_up