split setup command and add debugging
This commit is contained in:
519
common.nix
519
common.nix
@@ -321,6 +321,7 @@ isoConfig // {
|
||||
dnsutils
|
||||
dig
|
||||
gnutar
|
||||
openssl # Add this for certificate generation
|
||||
];
|
||||
|
||||
# System Setup Service (Root Tasks)
|
||||
@@ -444,25 +445,6 @@ isoConfig // {
|
||||
};
|
||||
};
|
||||
|
||||
# System Packages
|
||||
environment.systemPackages = with pkgs; [
|
||||
openssh
|
||||
curl
|
||||
git
|
||||
networkmanager
|
||||
docker
|
||||
docker-compose
|
||||
bash
|
||||
wget
|
||||
jq
|
||||
tree
|
||||
nano
|
||||
dnsutils
|
||||
dig
|
||||
gnutar
|
||||
openssl # Add this for certificate generation
|
||||
];
|
||||
|
||||
# Enhanced Bash Configuration with All Features
|
||||
programs.bash.interactiveShellInit =
|
||||
let
|
||||
@@ -520,63 +502,344 @@ isoConfig // {
|
||||
complete -F _workshop_completion deploy browser connect abra
|
||||
|
||||
# Core Workshop Functions
|
||||
setup() {
|
||||
echo "🔧 Setting up LOCAL Co-op Cloud environment..."
|
||||
setup() {
|
||||
echo "🔧 Setting up LOCAL Co-op Cloud environment..."
|
||||
|
||||
# Run permission checks first
|
||||
setup_permissions || return 1
|
||||
|
||||
# Run setup steps individually
|
||||
setup_dns || return 1
|
||||
setup_docker || return 1
|
||||
setup_abra_server || return 1
|
||||
setup_certificates || return 1
|
||||
setup_traefik || return 1
|
||||
|
||||
echo "🎉 Setup complete!"
|
||||
}
|
||||
|
||||
setup_permissions() {
|
||||
echo "🔐 Checking system permissions and prerequisites..."
|
||||
|
||||
# Check if running as workshop user
|
||||
if [[ "$(whoami)" != "workshop" ]]; then
|
||||
echo "⚠️ Not running as workshop user (current: $(whoami))"
|
||||
echo " This may cause permission issues. Consider running as workshop user."
|
||||
else
|
||||
echo "✅ Running as workshop user"
|
||||
fi
|
||||
|
||||
# Check sudo access
|
||||
if sudo -n true 2>/dev/null; then
|
||||
echo "✅ Sudo access available (no password required)"
|
||||
else
|
||||
echo "⚠️ Sudo may require password - this could interrupt automated setup"
|
||||
fi
|
||||
|
||||
# Check Docker group membership
|
||||
if id -nG | grep -q "docker"; then
|
||||
echo "✅ User is in docker group"
|
||||
else
|
||||
echo "⚠️ User not in docker group - Docker commands may fail"
|
||||
echo " Current groups: $(id -nG)"
|
||||
fi
|
||||
|
||||
# Check if abra is available
|
||||
if command -v abra >/dev/null 2>&1; then
|
||||
echo "✅ abra command found: $(which abra)"
|
||||
else
|
||||
echo "❌ abra command not found in PATH"
|
||||
echo " PATH: $PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check abra server configuration
|
||||
if sudo abra server ls 2>/dev/null | grep -q "default"; then
|
||||
echo "✅ Abra default server configured"
|
||||
else
|
||||
echo "⚠️ Abra default server not configured - will be set up"
|
||||
fi
|
||||
|
||||
# Check /tmp permissions
|
||||
if [[ -w "/tmp" ]]; then
|
||||
echo "✅ /tmp directory is writable"
|
||||
else
|
||||
echo "❌ /tmp directory is not writable"
|
||||
ls -ld /tmp
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check openssl availability
|
||||
if command -v openssl >/dev/null 2>&1; then
|
||||
echo "✅ OpenSSL available: $(openssl version | head -1)"
|
||||
else
|
||||
echo "❌ OpenSSL not found - certificate generation will fail"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check curl availability
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
echo "✅ curl available for health checks"
|
||||
else
|
||||
echo "⚠️ curl not found - health checks may not work properly"
|
||||
fi
|
||||
|
||||
echo "🎯 Permission checks complete!"
|
||||
}
|
||||
|
||||
setup_dns() {
|
||||
echo "🌐 Step 1: Verifying DNS configuration..."
|
||||
|
||||
# Verify DNS first
|
||||
if ! nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then
|
||||
echo "🔄 Restarting DNS..."
|
||||
echo "🔄 DNS not working, restarting dnsmasq..."
|
||||
sudo systemctl restart dnsmasq
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Ensure Docker Swarm + proxy network
|
||||
# Test again
|
||||
if nslookup traefik.workshop.local 127.0.0.1 >/dev/null 2>&1; then
|
||||
echo "✅ DNS restarted successfully"
|
||||
else
|
||||
echo "❌ DNS restart failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✅ DNS working correctly"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_docker() {
|
||||
echo "🐳 Step 2: Setting up Docker Swarm and networks..."
|
||||
|
||||
# Check Docker status
|
||||
if ! docker info 2>/dev/null | grep -q "Swarm: active"; then
|
||||
echo "🔥 Initializing Docker Swarm..."
|
||||
docker swarm init --advertise-addr 127.0.0.1
|
||||
if docker swarm init --advertise-addr 127.0.0.1; then
|
||||
echo "✅ Docker Swarm initialized"
|
||||
else
|
||||
echo "❌ Docker Swarm initialization failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✅ Docker Swarm already active"
|
||||
fi
|
||||
|
||||
# Check proxy network
|
||||
if ! docker network ls | grep -q "proxy"; then
|
||||
echo "🌐 Creating proxy network..."
|
||||
docker network create -d overlay proxy
|
||||
if docker network create -d overlay proxy; then
|
||||
echo "✅ Proxy network created"
|
||||
else
|
||||
echo "❌ Proxy network creation failed"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✅ Proxy network exists"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_abra_server() {
|
||||
echo "🗄️ Step 3: Setting up Abra server..."
|
||||
|
||||
# Add LOCAL server (critical difference!)
|
||||
if ! sudo abra server ls 2>/dev/null | grep -q "default"; then
|
||||
echo "🗄️ Adding LOCAL server..."
|
||||
sudo abra server add --local
|
||||
echo "✅ Local server registered"
|
||||
echo "🗄️ Adding LOCAL server to abra..."
|
||||
if sudo abra server add --local; then
|
||||
echo "✅ Local server registered"
|
||||
else
|
||||
echo "❌ Failed to add local server"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✅ Abra server already configured"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create self-signed certificate for offline use
|
||||
echo "🔐 Setting up self-signed certificates for offline use..."
|
||||
setup_certificates() {
|
||||
echo "🔐 Step 4: Generating self-signed certificates..."
|
||||
|
||||
# Create temporary cert directory
|
||||
CERT_DIR="/tmp/workshop-certs"
|
||||
mkdir -p $CERT_DIR
|
||||
setup_certificates_dir || return 1
|
||||
setup_certificates_generate || return 1
|
||||
setup_certificates_verify || return 1
|
||||
|
||||
# Generate self-signed certificate for *.workshop.local
|
||||
if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout "$CERT_DIR/workshop.key" \
|
||||
-out "$CERT_DIR/workshop.crt" \
|
||||
-subj "/CN=*.workshop.local" \
|
||||
-config <(printf "[req]\ndistinguished_name=req\n[v3_req]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\n") \
|
||||
-extensions v3_req 2>/dev/null || true
|
||||
echo "🔑 Generated self-signed certificate"
|
||||
fi
|
||||
# Export CERT_DIR for use in setup_traefik
|
||||
export CERT_DIR
|
||||
}
|
||||
|
||||
# Create and configure Traefik for OFFLINE mode
|
||||
if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then
|
||||
echo "🚀 Creating Traefik app for OFFLINE use..."
|
||||
sudo abra app new traefik --domain=traefik.workshop.local --server=default
|
||||
setup_certificates_dir() {
|
||||
echo "📁 Creating certificate directory..."
|
||||
|
||||
# Configure traefik for offline/local development
|
||||
TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env"
|
||||
CERT_DIR="/tmp/workshop-certs"
|
||||
echo " Target directory: $CERT_DIR"
|
||||
|
||||
echo "⚙️ Configuring Traefik for offline mode..."
|
||||
# Create offline-friendly traefik configuration
|
||||
sudo tee -a "$TRAEFIK_ENV" >/dev/null <<EOF
|
||||
# Check if directory already exists and clean it up
|
||||
if [[ -d "$CERT_DIR" ]]; then
|
||||
echo " 🧹 Cleaning up existing certificate directory..."
|
||||
rm -rf "$CERT_DIR" || {
|
||||
echo "❌ Failed to remove existing directory"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Create fresh directory
|
||||
if mkdir -p "$CERT_DIR"; then
|
||||
echo "✅ Certificate directory created"
|
||||
else
|
||||
echo "❌ Failed to create certificate directory"
|
||||
echo " Current user: $(whoami)"
|
||||
echo " User ID: $(id)"
|
||||
echo " /tmp permissions: $(ls -ld /tmp)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify directory permissions
|
||||
echo "🔍 Verifying directory permissions..."
|
||||
ls -la /tmp/ | grep workshop-certs || {
|
||||
echo "❌ Directory not found in /tmp listing"
|
||||
return 1
|
||||
}
|
||||
|
||||
local dir_perms=$(stat -c "%a" "$CERT_DIR" 2>/dev/null || echo "unknown")
|
||||
echo " Directory permissions: $dir_perms"
|
||||
echo " Directory owner: $(stat -c "%U:%G" "$CERT_DIR" 2>/dev/null || echo "unknown")"
|
||||
}
|
||||
|
||||
setup_certificates_generate() {
|
||||
echo "🔑 Generating self-signed certificate..."
|
||||
|
||||
CERT_FILE="$CERT_DIR/workshop.crt"
|
||||
KEY_FILE="$CERT_DIR/workshop.key"
|
||||
|
||||
echo " Certificate file: $CERT_FILE"
|
||||
echo " Key file: $KEY_FILE"
|
||||
|
||||
# Check if openssl is available
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "❌ OpenSSL not found in PATH"
|
||||
which openssl || echo " openssl command not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo " OpenSSL version: $(openssl version)"
|
||||
|
||||
# Check if certificate already exists
|
||||
if [[ -f "$CERT_FILE" ]]; then
|
||||
echo " ⚠️ Certificate file already exists, removing..."
|
||||
rm -f "$CERT_FILE" "$KEY_FILE" || {
|
||||
echo "❌ Failed to remove existing certificate files"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Generate certificate with detailed output
|
||||
echo " Generating RSA key and certificate..."
|
||||
if openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout "$KEY_FILE" \
|
||||
-out "$CERT_FILE" \
|
||||
-subj "/CN=*.workshop.local" \
|
||||
-config <(printf "[req]\ndistinguished_name=req\n[v3_req]\nsubjectAltName=DNS:*.workshop.local,DNS:workshop.local,DNS:localhost\n") \
|
||||
-extensions v3_req; then
|
||||
|
||||
echo "✅ Certificate generation completed successfully"
|
||||
else
|
||||
echo "❌ Certificate generation failed"
|
||||
echo " OpenSSL exit code: $?"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
setup_certificates_verify() {
|
||||
echo "🔍 Verifying certificate files..."
|
||||
|
||||
CERT_FILE="$CERT_DIR/workshop.crt"
|
||||
KEY_FILE="$CERT_DIR/workshop.key"
|
||||
|
||||
# Check if files exist
|
||||
if [[ ! -f "$CERT_FILE" ]]; then
|
||||
echo "❌ Certificate file not found: $CERT_FILE"
|
||||
ls -la "$CERT_DIR" || echo " Directory listing failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$KEY_FILE" ]]; then
|
||||
echo "❌ Key file not found: $KEY_FILE"
|
||||
ls -la "$CERT_DIR" || echo " Directory listing failed"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "✅ Certificate files created successfully"
|
||||
|
||||
# Show file details
|
||||
echo " Certificate file details:"
|
||||
ls -la "$CERT_FILE"
|
||||
echo " Key file details:"
|
||||
ls -la "$KEY_FILE"
|
||||
|
||||
# Verify certificate content
|
||||
echo " Verifying certificate content..."
|
||||
if openssl x509 -in "$CERT_FILE" -text -noout >/dev/null 2>&1; then
|
||||
echo "✅ Certificate is valid X.509 format"
|
||||
# Show certificate subject
|
||||
openssl x509 -in "$CERT_FILE" -subject -noout 2>/dev/null || echo " Could not read certificate subject"
|
||||
else
|
||||
echo "❌ Certificate file is not valid"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify key content
|
||||
if openssl rsa -in "$KEY_FILE" -check -noout >/dev/null 2>&1; then
|
||||
echo "✅ Private key is valid"
|
||||
else
|
||||
echo "❌ Private key is invalid"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "🎉 Certificate generation and verification complete!"
|
||||
}
|
||||
|
||||
setup_traefik() {
|
||||
echo "🚀 Step 5: Setting up Traefik..."
|
||||
|
||||
setup_traefik_app || return 1
|
||||
setup_traefik_config || return 1
|
||||
setup_traefik_secrets || return 1
|
||||
setup_traefik_deploy || return 1
|
||||
setup_traefik_wait || return 1
|
||||
}
|
||||
|
||||
setup_traefik_app() {
|
||||
echo "📦 Checking Traefik app..."
|
||||
|
||||
if ! sudo abra app ls 2>/dev/null | grep -q "traefik"; then
|
||||
echo "🚀 Creating Traefik app for OFFLINE use..."
|
||||
echo " Command: sudo abra app new traefik --domain=traefik.workshop.local --server=default"
|
||||
|
||||
if sudo abra app new traefik --domain=traefik.workshop.local --server=default; then
|
||||
echo "✅ Traefik app created successfully"
|
||||
else
|
||||
echo "❌ Failed to create Traefik app"
|
||||
echo " abra exit code: $?"
|
||||
sudo abra app ls 2>&1 || echo " Could not list apps"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
echo "✅ Traefik app already exists"
|
||||
fi
|
||||
}
|
||||
|
||||
setup_traefik_config() {
|
||||
echo "⚙️ Configuring Traefik for offline mode..."
|
||||
|
||||
TRAEFIK_ENV="/root/.abra/servers/default/traefik.workshop.local.env"
|
||||
echo " Config file: $TRAEFIK_ENV"
|
||||
|
||||
# Check if config file exists
|
||||
if [[ -f "$TRAEFIK_ENV" ]]; then
|
||||
echo " ⚠️ Config file already exists, backing up..."
|
||||
cp "$TRAEFIK_ENV" "$TRAEFIK_ENV.backup" || echo " Backup failed, continuing..."
|
||||
fi
|
||||
|
||||
# Create offline-friendly traefik configuration
|
||||
echo " Writing offline configuration..."
|
||||
if sudo tee -a "$TRAEFIK_ENV" >/dev/null <<EOF
|
||||
|
||||
# OFFLINE/LOCAL DEVELOPMENT CONFIGURATION
|
||||
LETS_ENCRYPT_ENV=staging
|
||||
@@ -589,36 +852,132 @@ COMPOSE_FILE="\$COMPOSE_FILE:compose.wildcard.yml"
|
||||
TRAEFIK_ACME_CASERVER=
|
||||
TRAEFIK_ACME_EMAIL=
|
||||
EOF
|
||||
then
|
||||
echo "✅ Traefik configuration written successfully"
|
||||
echo " Config file contents:"
|
||||
sudo cat "$TRAEFIK_ENV" | head -20
|
||||
else
|
||||
echo "❌ Failed to write Traefik configuration"
|
||||
echo " Target file: $TRAEFIK_ENV"
|
||||
ls -la "$(dirname "$TRAEFIK_ENV")" 2>/dev/null || echo " Parent directory not accessible"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Insert self-signed certificates as Docker secrets
|
||||
if [[ -f "$CERT_DIR/workshop.crt" && -f "$CERT_DIR/workshop.key" ]]; then
|
||||
echo "📋 Installing self-signed certificates..."
|
||||
sudo abra app secret insert traefik.workshop.local ssl_cert v1 -f < "$CERT_DIR/workshop.crt"
|
||||
sudo abra app secret insert traefik.workshop.local ssl_key v1 -f < "$CERT_DIR/workshop.key"
|
||||
fi
|
||||
setup_traefik_secrets() {
|
||||
echo "📋 Installing self-signed certificates as Docker secrets..."
|
||||
|
||||
echo "🚀 Deploying Traefik..."
|
||||
sudo abra app deploy traefik.workshop.local
|
||||
# Verify certificate files exist
|
||||
if [[ ! -f "$CERT_DIR/workshop.crt" ]]; then
|
||||
echo "❌ Certificate file not found: $CERT_DIR/workshop.crt"
|
||||
ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "⏳ Waiting for Traefik..."
|
||||
for i in {1..30}; do
|
||||
if curl -s -k https://traefik.workshop.local/ping >/dev/null 2>&1 || \
|
||||
curl -s http://traefik.workshop.local/ping >/dev/null 2>&1; then
|
||||
echo "✅ Traefik ready! Dashboard: https://traefik.workshop.local (accept self-signed cert)"
|
||||
echo "💡 For HTTP: http://traefik.workshop.local"
|
||||
return 0
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [[ ! -f "$CERT_DIR/workshop.key" ]]; then
|
||||
echo "❌ Key file not found: $CERT_DIR/workshop.key"
|
||||
ls -la "$CERT_DIR" 2>/dev/null || echo " Certificate directory not accessible"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "⚠️ Traefik may still be starting. Check: sudo abra app logs traefik.workshop.local"
|
||||
else
|
||||
echo "✅ Traefik already exists"
|
||||
fi
|
||||
echo " Certificate files verified:"
|
||||
ls -la "$CERT_DIR/workshop.crt" "$CERT_DIR/workshop.key"
|
||||
|
||||
# Cleanup temporary certs
|
||||
rm -rf "$CERT_DIR" 2>/dev/null || true
|
||||
}
|
||||
# Insert SSL certificate secret
|
||||
echo " 🔐 Inserting SSL certificate secret..."
|
||||
echo " Command: sudo abra app secret insert traefik.workshop.local ssl_cert v1"
|
||||
|
||||
if sudo abra app secret insert traefik.workshop.local ssl_cert v1 -f < "$CERT_DIR/workshop.crt"; then
|
||||
echo "✅ SSL certificate secret inserted successfully"
|
||||
else
|
||||
echo "❌ Failed to insert SSL certificate secret"
|
||||
echo " abra exit code: $?"
|
||||
echo " Checking abra app status..."
|
||||
sudo abra app ls 2>&1 || echo " Could not list apps"
|
||||
echo " Checking certificate file..."
|
||||
file "$CERT_DIR/workshop.crt" 2>/dev/null || echo " Could not check certificate file type"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Insert SSL key secret
|
||||
echo " 🔑 Inserting SSL key secret..."
|
||||
echo " Command: sudo abra app secret insert traefik.workshop.local ssl_key v1"
|
||||
|
||||
if sudo abra app secret insert traefik.workshop.local ssl_key v1 -f < "$CERT_DIR/workshop.key"; then
|
||||
echo "✅ SSL key secret inserted successfully"
|
||||
else
|
||||
echo "❌ Failed to insert SSL key secret"
|
||||
echo " abra exit code: $?"
|
||||
echo " Checking abra app status..."
|
||||
sudo abra app ls 2>&1 || echo " Could not list apps"
|
||||
echo " Checking key file..."
|
||||
file "$CERT_DIR/workshop.key" 2>/dev/null || echo " Could not check key file type"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "🎉 All secrets inserted successfully!"
|
||||
}
|
||||
|
||||
setup_traefik_deploy() {
|
||||
echo "🚀 Deploying Traefik..."
|
||||
|
||||
echo " Command: sudo abra app deploy traefik.workshop.local"
|
||||
|
||||
if sudo abra app deploy traefik.workshop.local; then
|
||||
echo "✅ Traefik deployment initiated successfully"
|
||||
else
|
||||
echo "❌ Traefik deployment failed"
|
||||
echo " abra exit code: $?"
|
||||
echo " Checking deployment status..."
|
||||
sudo abra app ps traefik.workshop.local 2>&1 || echo " Could not check app status"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
setup_traefik_wait() {
|
||||
echo "⏳ Waiting for Traefik to be ready..."
|
||||
|
||||
for i in {1..30}; do
|
||||
echo " Checking Traefik status (attempt $i/30)..."
|
||||
|
||||
# Try HTTPS first
|
||||
if curl -s -k --max-time 5 https://traefik.workshop.local/ping >/dev/null 2>&1; then
|
||||
echo "✅ Traefik ready via HTTPS!"
|
||||
echo " Dashboard: https://traefik.workshop.local (accept self-signed cert)"
|
||||
echo " 💡 For HTTP: http://traefik.workshop.local"
|
||||
break
|
||||
fi
|
||||
|
||||
# Try HTTP as fallback
|
||||
if curl -s --max-time 5 http://traefik.workshop.local/ping >/dev/null 2>&1; then
|
||||
echo "✅ Traefik ready via HTTP!"
|
||||
echo " Dashboard: http://traefik.workshop.local"
|
||||
echo " 💡 For HTTPS: https://traefik.workshop.local (may require accepting cert)"
|
||||
break
|
||||
fi
|
||||
|
||||
if [[ $i -eq 30 ]]; then
|
||||
echo "❌ Traefik failed to respond after 30 attempts"
|
||||
echo " 🔍 Debug commands:"
|
||||
echo " sudo abra app logs traefik.workshop.local"
|
||||
echo " sudo abra app ps traefik.workshop.local"
|
||||
echo " docker service ls | grep traefik"
|
||||
return 1
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Cleanup temporary certs
|
||||
echo "🧹 Cleaning up temporary certificate files..."
|
||||
if rm -rf "$CERT_DIR" 2>/dev/null; then
|
||||
echo "✅ Certificate cleanup completed"
|
||||
else
|
||||
echo "⚠️ Certificate cleanup failed (non-critical)"
|
||||
fi
|
||||
|
||||
echo "🎉 Traefik setup complete!"
|
||||
}
|
||||
|
||||
deploy() {
|
||||
if [[ -z "$1" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user