Initial commit: loc_az_hci (smom-dbis-138 excluded via .gitignore)
Some checks failed
Test / test (push) Has been cancelled

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-08 09:04:46 -08:00
commit c39465c2bd
386 changed files with 50649 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
#!/bin/bash
source ~/.bashrc
# Add SSH Keys to VMs via Proxmox API
# Configures SSH keys for ubuntu user in all VMs
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
SSH_KEY_FILE="$HOME/.ssh/id_ed25519_proxmox.pub"
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
add_ssh_key_to_vm() {
local vmid=$1
local name=$2
log_info "Adding SSH key to VM $vmid ($name)..."
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
if [ -z "$ticket" ] || [ -z "$csrf_token" ]; then
log_error "Failed to get API tokens"
return 1
fi
if [ ! -f "$SSH_KEY_FILE" ]; then
log_error "SSH key file not found: $SSH_KEY_FILE"
return 1
fi
# Read and encode SSH key
local ssh_key_content=$(cat "$SSH_KEY_FILE")
local ssh_key_b64=$(echo "$ssh_key_content" | base64 -w 0)
# Add SSH key via cloud-init
local result=$(curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
--data-urlencode "sshkeys=$ssh_key_b64" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$result" | grep -q '"data"'; then
log_info "✓ SSH key added to VM $vmid"
return 0
else
log_error "Failed to add SSH key: $result"
return 1
fi
}
reboot_vm() {
local vmid=$1
local name=$2
log_info "Rebooting VM $vmid ($name) to apply SSH key..."
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/reboot" > /dev/null
log_info "VM $vmid rebooted"
}
main() {
log_info "Adding SSH Keys to VMs"
echo ""
if [ ! -f "$SSH_KEY_FILE" ]; then
log_error "SSH key file not found: $SSH_KEY_FILE"
log_info "Run: ./scripts/utils/setup-ssh-keys.sh"
exit 1
fi
local vms=(
"100 cloudflare-tunnel"
"101 k3s-master"
"102 git-server"
"103 observability"
)
# Add SSH keys
for vm_spec in "${vms[@]}"; do
read -r vmid name <<< "$vm_spec"
add_ssh_key_to_vm "$vmid" "$name"
done
echo ""
log_info "Rebooting VMs to apply SSH keys..."
for vm_spec in "${vms[@]}"; do
read -r vmid name <<< "$vm_spec"
reboot_vm "$vmid" "$name"
sleep 2
done
log_info ""
log_info "SSH keys added. Wait 2-3 minutes for VMs to reboot, then test:"
# Try to show discovered IPs (if guest agent is working)
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
for vm_spec in "${vms[@]}"; do
read -r vmid name <<< "$vm_spec"
local ip
ip="$(get_vm_ip_from_guest_agent "$vmid" || true)"
if [[ -n "$ip" ]]; then
log_info " ssh -i ~/.ssh/id_ed25519_proxmox ubuntu@$ip # VM $vmid ($name)"
fi
done
else
log_info " ssh -i ~/.ssh/id_ed25519_proxmox ubuntu@<VM_IP>"
log_info " (Use Proxmox Summary or router to find VM IPs)"
fi
}
main "$@"

View File

@@ -0,0 +1,133 @@
#!/bin/bash
source ~/.bashrc
# Complete All Deployments: Gitea, Observability, Cloudflare, GitOps, Security
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_section() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
main() {
log_section "Complete Deployment - All Services"
local errors=0
# 1. Deploy Gitea
log_section "1. Deploying Gitea on VM 102"
if bash "$SCRIPT_DIR/deploy-gitea.sh"; then
log_info "✓ Gitea deployment completed"
else
log_error "✗ Gitea deployment failed"
errors=$((errors + 1))
fi
sleep 2
# 2. Deploy Observability Stack
log_section "2. Deploying Observability Stack on VM 103"
if bash "$SCRIPT_DIR/deploy-observability.sh"; then
log_info "✓ Observability deployment completed"
else
log_error "✗ Observability deployment failed"
errors=$((errors + 1))
fi
sleep 2
# 3. Configure Cloudflare Tunnel
log_section "3. Configuring Cloudflare Tunnel on VM 100"
log_warn "Note: This requires interactive browser authentication"
if bash "$SCRIPT_DIR/configure-cloudflare-tunnel.sh"; then
log_info "✓ Cloudflare Tunnel configuration completed"
else
log_error "✗ Cloudflare Tunnel configuration failed"
errors=$((errors + 1))
fi
sleep 2
# 4. Configure GitOps Workflows
log_section "4. Configuring GitOps Workflows on VM 101"
if bash "$SCRIPT_DIR/configure-gitops-workflows.sh"; then
log_info "✓ GitOps workflows configuration completed"
else
log_error "✗ GitOps workflows configuration failed"
errors=$((errors + 1))
fi
sleep 2
# 5. Security Hardening - RBAC
log_section "5. Setting up Proxmox RBAC"
if bash "$PROJECT_ROOT/scripts/security/setup-proxmox-rbac.sh"; then
log_info "✓ RBAC setup completed"
else
log_error "✗ RBAC setup failed"
errors=$((errors + 1))
fi
sleep 2
# 6. Security Hardening - Firewall
log_section "6. Configuring Firewall Rules"
if bash "$PROJECT_ROOT/scripts/security/configure-firewall-rules.sh"; then
log_info "✓ Firewall configuration completed"
else
log_error "✗ Firewall configuration failed"
errors=$((errors + 1))
fi
# Summary
log_section "Deployment Summary"
if [ $errors -eq 0 ]; then
log_info "✓ All deployments completed successfully!"
echo ""
log_info "Service URLs:"
log_info " Gitea: http://192.168.1.121:3000"
log_info " Prometheus: http://192.168.1.82:9090"
log_info " Grafana: http://192.168.1.82:3000 (admin/admin)"
echo ""
log_info "Next steps:"
log_info "1. Complete Gitea first-time setup at http://192.168.1.121:3000"
log_info "2. Change Grafana password at http://192.168.1.82:3000"
log_info "3. Configure Cloudflare DNS records (see Cloudflare Tunnel output)"
log_info "4. Configure Zero Trust policies in Cloudflare Dashboard"
log_info "5. Create GitOps repository and push manifests"
else
log_error "✗ Some deployments failed ($errors errors)"
log_info "Review the output above for details"
exit 1
fi
}
main "$@"

View File

@@ -0,0 +1,229 @@
#!/bin/bash
source ~/.bashrc
# Complete All Infrastructure Setup
# Sets up cluster, storage, and network on both Proxmox hosts
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
ML110_IP="${PROXMOX_ML110_IP:-192.168.1.206}"
R630_IP="${PROXMOX_R630_IP:-192.168.1.49}"
SSH_KEY="$HOME/.ssh/id_ed25519_proxmox"
SSH_OPTS="-i $SSH_KEY"
execute_remote() {
local host=$1
local command=$2
local description=$3
log_info "$description on $host"
if ssh $SSH_OPTS -o StrictHostKeyChecking=no "root@$host" "$command"; then
log_info "$description completed on $host"
return 0
else
log_error "$description failed on $host"
return 1
fi
}
copy_file_remote() {
local host=$1
local source=$2
local dest=$3
log_info "Copying $source to root@$host:$dest"
scp $SSH_OPTS "$source" "root@$host:$dest"
}
# Step 1: Create cluster on ML110
create_cluster_ml110() {
log_step "Creating Proxmox Cluster on ML110"
# Check if cluster already exists
if ssh $SSH_OPTS "root@$ML110_IP" "pvecm status" &>/dev/null; then
log_warn "Cluster already exists on ML110"
ssh $SSH_OPTS "root@$ML110_IP" "pvecm status"
return 0
fi
# Copy cluster setup script
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/proxmox/cluster-setup.sh" "/tmp/cluster-setup.sh"
# Execute cluster creation
execute_remote "$ML110_IP" \
"chmod +x /tmp/cluster-setup.sh && CLUSTER_NAME=hc-cluster NODE_ROLE=create /tmp/cluster-setup.sh" \
"Cluster creation"
# Verify
execute_remote "$ML110_IP" "pvecm status && pvecm nodes" "Cluster verification"
}
# Step 2: Join R630 to cluster
join_cluster_r630() {
log_step "Joining R630 to Proxmox Cluster"
# Check if already in cluster
if ssh $SSH_OPTS "root@$R630_IP" "pvecm status" &>/dev/null; then
log_warn "R630 already in cluster"
return 0
fi
# Copy cluster setup script
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/proxmox/cluster-setup.sh" "/tmp/cluster-setup.sh"
# Execute cluster join
if [ -n "$PVE_ROOT_PASS" ]; then
execute_remote "$R630_IP" \
"chmod +x /tmp/cluster-setup.sh && CLUSTER_NAME=hc-cluster NODE_ROLE=join CLUSTER_NODE_IP=$ML110_IP ROOT_PASSWORD='$PVE_ROOT_PASS' /tmp/cluster-setup.sh" \
"Cluster join"
else
log_error "PVE_ROOT_PASS not set. Cannot join cluster without root password."
return 1
fi
}
# Step 3: Configure NFS storage on ML110
configure_nfs_ml110() {
log_step "Configuring NFS Storage on ML110"
# Check if storage already exists
if ssh $SSH_OPTS "root@$ML110_IP" "pvesm status | grep router-storage" &>/dev/null; then
log_warn "NFS storage already configured on ML110"
return 0
fi
# Copy NFS storage script
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/proxmox/nfs-storage.sh" "/tmp/nfs-storage.sh"
# Execute NFS configuration
execute_remote "$ML110_IP" \
"chmod +x /tmp/nfs-storage.sh && NFS_SERVER=10.10.10.1 NFS_PATH=/mnt/storage STORAGE_NAME=router-storage /tmp/nfs-storage.sh" \
"NFS storage configuration"
# Verify
execute_remote "$ML110_IP" "pvesm status" "NFS storage verification"
}
# Step 4: Configure NFS storage on R630
configure_nfs_r630() {
log_step "Configuring NFS Storage on R630"
# Check if storage already exists
if ssh $SSH_OPTS "root@$R630_IP" "pvesm status | grep router-storage" &>/dev/null; then
log_warn "NFS storage already configured on R630"
return 0
fi
# Copy NFS storage script
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/proxmox/nfs-storage.sh" "/tmp/nfs-storage.sh"
# Execute NFS configuration
execute_remote "$R630_IP" \
"chmod +x /tmp/nfs-storage.sh && NFS_SERVER=10.10.10.1 NFS_PATH=/mnt/storage STORAGE_NAME=router-storage /tmp/nfs-storage.sh" \
"NFS storage configuration"
# Verify
execute_remote "$R630_IP" "pvesm status" "NFS storage verification"
}
# Step 5: Configure VLAN bridges on ML110
configure_vlans_ml110() {
log_step "Configuring VLAN Bridges on ML110"
# Copy VLAN script
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" "/tmp/configure-proxmox-vlans.sh"
# Execute VLAN configuration
execute_remote "$ML110_IP" \
"chmod +x /tmp/configure-proxmox-vlans.sh && /tmp/configure-proxmox-vlans.sh && systemctl restart networking" \
"VLAN configuration"
# Verify
execute_remote "$ML110_IP" "ip addr show | grep -E 'vmbr[0-9]+' | head -10" "VLAN verification"
}
# Step 6: Configure VLAN bridges on R630
configure_vlans_r630() {
log_step "Configuring VLAN Bridges on R630"
# Copy VLAN script
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" "/tmp/configure-proxmox-vlans.sh"
# Execute VLAN configuration
execute_remote "$R630_IP" \
"chmod +x /tmp/configure-proxmox-vlans.sh && /tmp/configure-proxmox-vlans.sh && systemctl restart networking" \
"VLAN configuration"
# Verify
execute_remote "$R630_IP" "ip addr show | grep -E 'vmbr[0-9]+' | head -10" "VLAN verification"
}
main() {
log_info "Completing All Infrastructure Setup"
echo ""
# Check SSH access
if [ ! -f "$SSH_KEY" ]; then
log_error "SSH key not found: $SSH_KEY"
log_info "Run: ./scripts/utils/setup-ssh-keys.sh"
exit 1
fi
if ! ssh $SSH_OPTS -o ConnectTimeout=5 "root@$ML110_IP" "echo 'SSH OK'" &> /dev/null; then
log_error "SSH access to ML110 failed"
exit 1
fi
# Infrastructure setup
create_cluster_ml110
configure_nfs_ml110
configure_vlans_ml110
# R630 setup (if SSH available)
if ssh $SSH_OPTS -o ConnectTimeout=5 "root@$R630_IP" "echo 'SSH OK'" &> /dev/null; then
join_cluster_r630
configure_nfs_r630
configure_vlans_r630
else
log_warn "SSH access to R630 not available, skipping R630 setup"
fi
log_step "Infrastructure Setup Complete!"
log_info "Next: Verify VM boot and network connectivity"
}
main "$@"

View File

@@ -0,0 +1,285 @@
#!/bin/bash
source ~/.bashrc
# Master Orchestration Script - Complete All Next Steps
# Executes all deployment steps in recommended order
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
# Check prerequisites
check_prerequisites() {
log_step "Checking Prerequisites"
if [ ! -f "$SSH_KEY" ]; then
log_error "SSH key not found: $SSH_KEY"
exit 1
fi
if [ ! -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
log_error "Helper library not found"
exit 1
fi
log_info "Prerequisites check passed"
}
# Step 1: Manual SSH Fix
step1_ssh_fix() {
log_step "Step 1: Fix SSH Access to VMs (MANUAL)"
log_warn "This step requires manual intervention via Proxmox Console"
echo ""
log_info "Running SSH fix instructions script..."
"$PROJECT_ROOT/scripts/fix/fix-vm-ssh-via-console.sh"
echo ""
log_info "After fixing SSH manually, press Enter to continue..."
read -r
# Test SSH access
log_info "Testing SSH access..."
local all_ok=true
for ip in 192.168.1.60 192.168.1.188 192.168.1.121 192.168.1.82; do
if ssh -i "$SSH_KEY" -o ConnectTimeout=5 -o StrictHostKeyChecking=no ubuntu@$ip "echo 'SSH OK'" &>/dev/null; then
log_info " $ip: ✓ SSH working"
else
log_error " $ip: ✗ SSH not working"
all_ok=false
fi
done
if [ "$all_ok" = false ]; then
log_error "SSH access not working for all VMs. Please fix SSH access first."
exit 1
fi
log_info "✓ SSH access verified for all VMs"
}
# Step 2: Install QEMU Guest Agent
step2_install_qga() {
log_step "Step 2: Install QEMU Guest Agent"
if [ ! -f "$PROJECT_ROOT/scripts/infrastructure/install-qemu-guest-agent.sh" ]; then
log_error "QGA installation script not found"
return 1
fi
"$PROJECT_ROOT/scripts/infrastructure/install-qemu-guest-agent.sh"
log_info "✓ QEMU Guest Agent installation complete"
}
# Step 3: Deploy Services
step3_deploy_services() {
log_step "Step 3: Deploy Services"
# 3.1 Deploy Gitea
log_info "3.1 Deploying Gitea (VM 102)..."
if [ -f "$PROJECT_ROOT/scripts/deploy/deploy-gitea.sh" ]; then
"$PROJECT_ROOT/scripts/deploy/deploy-gitea.sh"
else
log_warn "Gitea deployment script not found, skipping"
fi
echo ""
# 3.2 Deploy Observability
log_info "3.2 Deploying Observability Stack (VM 103)..."
if [ -f "$PROJECT_ROOT/scripts/deploy/deploy-observability.sh" ]; then
"$PROJECT_ROOT/scripts/deploy/deploy-observability.sh"
else
log_warn "Observability deployment script not found, skipping"
fi
echo ""
# 3.3 Verify K3s
log_info "3.3 Verifying K3s (VM 101)..."
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
local k3s_ip
k3s_ip="$(get_vm_ip_or_warn 101 "k3s-master" || true)"
if [[ -n "$k3s_ip" ]]; then
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no ubuntu@$k3s_ip "sudo kubectl get nodes" &>/dev/null; then
log_info "✓ K3s is running"
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no ubuntu@$k3s_ip "sudo kubectl get nodes"
else
log_warn "K3s may not be fully configured"
fi
fi
log_info "✓ Service deployment complete"
}
# Step 4: Join R630 to Cluster
step4_join_r630() {
log_step "Step 4: Join R630 to Cluster"
log_info "Checking SSH access to R630..."
if ssh -i "$SSH_KEY" -o ConnectTimeout=5 root@192.168.1.49 "echo 'SSH OK'" &>/dev/null; then
log_info "✓ SSH to R630 is working"
log_info "Joining R630 to cluster..."
ssh -i "$SSH_KEY" root@192.168.1.49 <<EOF
cd /home/intlc/projects/loc_az_hci
export CLUSTER_NAME=hc-cluster
export NODE_ROLE=join
export CLUSTER_NODE_IP=192.168.1.206
export ROOT_PASSWORD=${PVE_ROOT_PASS:-}
./infrastructure/proxmox/cluster-setup.sh
EOF
log_info "Verifying cluster status..."
ssh -i "$SSH_KEY" root@192.168.1.49 "pvecm status"
log_info "✓ R630 joined to cluster"
else
log_warn "SSH to R630 not working. Please:"
log_info " 1. Enable SSH on R630: https://192.168.1.49:8006 → System → Services → ssh"
log_info " 2. Add SSH key: ssh-copy-id -i $SSH_KEY.pub root@192.168.1.49"
log_info " 3. Re-run this script"
fi
}
# Step 5: Configure NFS Storage
step5_configure_nfs() {
log_step "Step 5: Configure NFS Storage"
local nfs_server="${NFS_SERVER:-10.10.10.1}"
log_info "Checking NFS server reachability: $nfs_server"
if ping -c 1 -W 2 "$nfs_server" &>/dev/null; then
log_info "✓ NFS server is reachable"
# Configure on ML110
log_info "Configuring NFS on ML110..."
ssh -i "$SSH_KEY" root@192.168.1.206 <<EOF
cd /home/intlc/projects/loc_az_hci
export NFS_SERVER=$nfs_server
export NFS_PATH=/mnt/storage
export STORAGE_NAME=router-storage
./infrastructure/proxmox/nfs-storage.sh
EOF
# Configure on R630 (if SSH working)
if ssh -i "$SSH_KEY" -o ConnectTimeout=5 root@192.168.1.49 "echo 'SSH OK'" &>/dev/null; then
log_info "Configuring NFS on R630..."
ssh -i "$SSH_KEY" root@192.168.1.49 <<EOF
cd /home/intlc/projects/loc_az_hci
export NFS_SERVER=$nfs_server
export NFS_PATH=/mnt/storage
export STORAGE_NAME=router-storage
./infrastructure/proxmox/nfs-storage.sh
EOF
fi
log_info "Verifying NFS storage..."
ssh -i "$SSH_KEY" root@192.168.1.206 "pvesm status | grep router-storage || echo 'NFS storage not found'"
log_info "✓ NFS storage configured"
else
log_warn "NFS server ($nfs_server) is not reachable. Skipping NFS configuration."
fi
}
# Step 6: Configure VLAN Bridges on R630
step6_configure_vlans() {
log_step "Step 6: Configure VLAN Bridges on R630"
if ssh -i "$SSH_KEY" -o ConnectTimeout=5 root@192.168.1.49 "echo 'SSH OK'" &>/dev/null; then
log_info "Configuring VLAN bridges on R630..."
ssh -i "$SSH_KEY" root@192.168.1.49 <<EOF
cd /home/intlc/projects/loc_az_hci
./infrastructure/network/configure-proxmox-vlans.sh
systemctl restart networking
EOF
log_info "Verifying VLAN bridges..."
ssh -i "$SSH_KEY" root@192.168.1.49 "ip addr show | grep -E 'vmbr[0-9]+'"
log_info "✓ VLAN bridges configured"
else
log_warn "SSH to R630 not working. Skipping VLAN configuration."
fi
}
# Final status report
final_status() {
log_step "Final Status Report"
log_info "Checking cluster status..."
ssh -i "$SSH_KEY" root@192.168.1.206 "pvecm status" 2>/dev/null || log_warn "Could not get cluster status"
echo ""
log_info "Checking VM status..."
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
for vmid in 100 101 102 103; do
local ip
ip="$(get_vm_ip_from_guest_agent "$vmid" 2>/dev/null || true)"
if [[ -n "$ip" ]]; then
log_info " VM $vmid: ✓ Running (IP: $ip)"
else
log_warn " VM $vmid: Could not get IP"
fi
done
echo ""
log_info "Service URLs:"
log_info " Gitea: http://192.168.1.121:3000"
log_info " Prometheus: http://192.168.1.82:9090"
log_info " Grafana: http://192.168.1.82:3000 (admin/admin)"
echo ""
log_info "✓ Deployment complete!"
log_info "Next steps: Configure services (Gitea, Grafana, Cloudflare Tunnel)"
}
main() {
log_step "Complete Deployment - All Next Steps"
check_prerequisites
step1_ssh_fix
step2_install_qga
step3_deploy_services
step4_join_r630
step5_configure_nfs
step6_configure_vlans
final_status
}
main "$@"

View File

@@ -0,0 +1,323 @@
#!/bin/bash
source ~/.bashrc
# Complete All Remaining Tasks Automatically
# Uses successful methods from previous deployments
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "\n${BLUE}=== $1 ===${NC}"; }
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
VM_USER="${VM_USER:-ubuntu}"
# VM IPs (discovered earlier)
VM_100_IP="192.168.1.57" # cloudflare-tunnel
VM_101_IP="192.168.1.188" # k3s-master
VM_102_IP="192.168.1.121" # git-server
VM_103_IP="192.168.1.82" # observability
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
# Step 1: Install K3s on VM 101
install_k3s() {
log_step "Step 1: Installing K3s on VM 101 (k3s-master)"
log_info "Installing K3s on $VM_101_IP..."
ssh $SSH_OPTS "${VM_USER}@${VM_101_IP}" <<'K3S_EOF'
set -e
echo "=== Installing K3s ==="
# Check if already installed
if command -v k3s &>/dev/null; then
echo "K3s already installed"
k3s --version
sudo systemctl is-active k3s && echo "K3s is running" || echo "K3s is not running"
exit 0
fi
# Install K3s
echo "Downloading and installing K3s..."
curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=latest sh -
# Verify installation
if command -v k3s &>/dev/null; then
echo "K3s installed successfully"
k3s --version
# Start and enable service
sudo systemctl enable k3s
sudo systemctl start k3s
# Wait for service to be ready
echo "Waiting for K3s to start..."
sleep 15
# Verify service status
if sudo systemctl is-active --quiet k3s; then
echo "✓ K3s service is running"
sudo k3s kubectl get nodes
sudo k3s kubectl get pods --all-namespaces
else
echo "✗ K3s service failed to start"
sudo systemctl status k3s --no-pager | head -20
exit 1
fi
else
echo "✗ K3s installation failed"
exit 1
fi
K3S_EOF
if [ $? -eq 0 ]; then
log_info "✓ K3s installed and running on VM 101"
else
log_error "K3s installation failed"
return 1
fi
}
# Step 2: Install and Configure Cloudflare Tunnel on VM 100
install_cloudflare_tunnel() {
log_step "Step 2: Installing Cloudflare Tunnel on VM 100 (cloudflare-tunnel)"
local tunnel_token="${CLOUDFLARE_TUNNEL_TOKEN:-}"
if [ -z "$tunnel_token" ]; then
log_warn "CLOUDFLARE_TUNNEL_TOKEN not set. Skipping Cloudflare Tunnel configuration."
log_info "Installing cloudflared only..."
fi
log_info "Installing cloudflared on $VM_100_IP..."
ssh $SSH_OPTS "${VM_USER}@${VM_100_IP}" <<CLOUDFLARE_EOF
set -e
echo "=== Installing Cloudflare Tunnel ==="
# Install cloudflared
if ! command -v cloudflared &>/dev/null; then
echo "Downloading cloudflared..."
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /tmp/cloudflared
sudo mv /tmp/cloudflared /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared
cloudflared --version
echo "✓ cloudflared installed"
else
echo "cloudflared already installed"
cloudflared --version
fi
# Configure tunnel if token is provided
if [ -n "${tunnel_token}" ]; then
echo "Configuring Cloudflare Tunnel..."
sudo mkdir -p /etc/cloudflared
# Create config file
sudo tee /etc/cloudflared/config.yml > /dev/null <<CONFIG_EOF
tunnel: \$(cloudflared tunnel token ${tunnel_token} | grep -oP 'Tunnel ID: \K[^ ]+' || echo '')
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: grafana.${CLOUDFLARE_DOMAIN:-d-bis.org}
service: http://${VM_103_IP}:3000
- hostname: prometheus.${CLOUDFLARE_DOMAIN:-d-bis.org}
service: http://${VM_103_IP}:9090
- hostname: git.${CLOUDFLARE_DOMAIN:-d-bis.org}
service: http://${VM_102_IP}:3000
- hostname: proxmox.${CLOUDFLARE_DOMAIN:-d-bis.org}
service: https://${PROXMOX_HOST}:8006
- service: http_status:404
CONFIG_EOF
# Install as systemd service
sudo cloudflared service install ${tunnel_token}
# Start service
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sleep 5
if sudo systemctl is-active --quiet cloudflared; then
echo "✓ Cloudflare Tunnel service is running"
sudo systemctl status cloudflared --no-pager | head -10
else
echo "⚠ Cloudflare Tunnel service may need manual configuration"
sudo systemctl status cloudflared --no-pager | head -10
fi
else
echo "⚠ Tunnel token not provided. Install manually with:"
echo " cloudflared tunnel login"
echo " cloudflared tunnel create <tunnel-name>"
echo " cloudflared tunnel route dns <tunnel-name> <hostname>"
fi
CLOUDFLARE_EOF
if [ $? -eq 0 ]; then
log_info "✓ Cloudflare Tunnel installed on VM 100"
else
log_warn "Cloudflare Tunnel installation had issues (may need manual config)"
fi
}
# Step 3: Configure Gitea Initial Setup (via API)
configure_gitea() {
log_step "Step 3: Configuring Gitea Initial Setup"
log_info "Waiting for Gitea to be ready..."
local max_attempts=30
local attempt=0
local gitea_ready=false
while [ $attempt -lt $max_attempts ]; do
if curl -s "http://${VM_102_IP}:3000" | grep -q "Gitea"; then
gitea_ready=true
break
fi
sleep 2
attempt=$((attempt + 1))
done
if [ "$gitea_ready" = false ]; then
log_warn "Gitea not ready after $max_attempts attempts"
log_info "Gitea initial setup must be completed manually:"
log_info " 1. Visit http://${VM_102_IP}:3000"
log_info " 2. Complete the installation wizard"
return 0
fi
log_info "Gitea is ready. Attempting automated setup..."
# Try to configure via API (Gitea 1.19+ supports installation API)
local response=$(curl -s -X POST "http://${VM_102_IP}:3000/api/v1/setup" \
-H "Content-Type: application/json" \
-d '{
"db_type": "sqlite3",
"db_host": "",
"db_user": "",
"db_passwd": "",
"db_name": "gitea",
"ssl_mode": "disable",
"db_path": "data/gitea.db",
"app_name": "Gitea",
"repo_root_path": "/data/git/repositories",
"lfs_root_path": "/data/git/lfs",
"run_user": "git",
"domain": "'${VM_102_IP}'",
"ssh_port": 2222,
"http_port": 3000,
"app_url": "http://'${VM_102_IP}':3000/",
"log_root_path": "/data/gitea/log",
"smtp_host": "",
"smtp_from": "",
"smtp_user": "",
"smtp_passwd": "",
"admin_name": "admin",
"admin_passwd": "admin123",
"admin_confirm_passwd": "admin123",
"admin_email": "admin@'${CLOUDFLARE_DOMAIN:-d-bis.org}'"
}' 2>/dev/null || echo "")
if echo "$response" | grep -q "success\|created"; then
log_info "✓ Gitea configured successfully"
log_info " Admin user: admin"
log_info " Admin password: admin123 (change on first login!)"
else
log_warn "Automated Gitea setup may have failed"
log_info "Complete setup manually at http://${VM_102_IP}:3000"
log_info "Or check if setup was already completed"
fi
}
# Step 4: Final Status and Summary
final_summary() {
log_step "Final Summary"
echo ""
log_info "VM Status:"
ssh $SSH_OPTS "root@$PROXMOX_HOST" "qm list | grep -E '(100|101|102|103)'"
echo ""
log_info "Service Status:"
# Check K3s
if ssh $SSH_OPTS "${VM_USER}@${VM_101_IP}" "sudo systemctl is-active k3s &>/dev/null && echo 'active' || echo 'inactive'" | grep -q "active"; then
log_info " ✓ K3s (VM 101): Running"
ssh $SSH_OPTS "${VM_USER}@${VM_101_IP}" "sudo k3s kubectl get nodes 2>/dev/null | head -3" || true
else
log_warn " ✗ K3s (VM 101): Not running"
fi
# Check Cloudflare Tunnel
if ssh $SSH_OPTS "${VM_USER}@${VM_100_IP}" "sudo systemctl is-active cloudflared &>/dev/null && echo 'active' || echo 'inactive'" 2>/dev/null | grep -q "active"; then
log_info " ✓ Cloudflare Tunnel (VM 100): Running"
else
log_warn " ⚠ Cloudflare Tunnel (VM 100): May need manual configuration"
fi
# Check Gitea
if curl -s "http://${VM_102_IP}:3000" | grep -q "Gitea"; then
log_info " ✓ Gitea (VM 102): Running at http://${VM_102_IP}:3000"
else
log_warn " ✗ Gitea (VM 102): Not accessible"
fi
# Check Observability
if curl -s "http://${VM_103_IP}:9090/-/healthy" &>/dev/null; then
log_info " ✓ Prometheus (VM 103): Running at http://${VM_103_IP}:9090"
else
log_warn " ✗ Prometheus (VM 103): Not accessible"
fi
if curl -s "http://${VM_103_IP}:3000/api/health" &>/dev/null; then
log_info " ✓ Grafana (VM 103): Running at http://${VM_103_IP}:3000"
else
log_warn " ✗ Grafana (VM 103): Not accessible"
fi
echo ""
log_info "Service URLs:"
log_info " K3s Dashboard: Use 'kubectl' commands on VM 101"
log_info " Gitea: http://${VM_102_IP}:3000"
log_info " Prometheus: http://${VM_103_IP}:9090"
log_info " Grafana: http://${VM_103_IP}:3000 (admin/admin)"
echo ""
log_warn "Tasks Requiring Manual Steps or External Dependencies:"
log_info " 1. Join R630 to cluster: SSH to R630 (192.168.1.49) not accessible"
log_info " 2. Configure NFS storage: NFS server (10.10.10.1) not reachable"
log_info " 3. Configure VLAN bridges on R630: Requires SSH to R630"
log_info " 4. Complete Gitea setup: May need manual web UI access if API setup failed"
echo ""
log_info "✓ All automated tasks completed!"
}
main() {
log_step "Completing All Remaining Tasks"
install_k3s
install_cloudflare_tunnel
configure_gitea
final_summary
}
main "$@"

View File

@@ -0,0 +1,202 @@
#!/bin/bash
source ~/.bashrc
# Complete All Steps with Workarounds
# Attempts all possible steps, documents what requires manual intervention
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
# Step 1: Check and attach ISO to template
setup_template_iso() {
log_step "Step 1: Setting Up Template with ISO"
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Check for Ubuntu ISO
local isos=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/storage/local/content" | \
python3 -c "import sys, json; r=json.load(sys.stdin); isos=[i.get('volid', '') for i in r.get('data', []) if i.get('content')=='iso' and 'ubuntu' in i.get('volid', '').lower()]; print('\n'.join(isos[:1]))" 2>/dev/null)
if [ -n "$isos" ]; then
local iso_file=$(echo "$isos" | head -1)
log_info "Found Ubuntu ISO: $iso_file"
log_info "Attaching to template 9000..."
# Attach ISO and set boot order
local result=$(curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "ide2=$iso_file,media=cdrom" \
-d "boot=order=ide2;scsi0" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/9000/config" 2>&1)
if echo "$result" | grep -q '"data"'; then
log_info "✓ ISO attached successfully"
log_info "Template 9000 is ready for OS installation"
log_warn "Next: Start VM 9000 and install Ubuntu via console"
return 0
else
log_warn "Could not attach ISO via API: $result"
log_info "Manual step: Attach ISO via Proxmox Web UI"
return 1
fi
else
log_warn "No Ubuntu ISO found in storage"
log_info "Need to upload Ubuntu 24.04 ISO first"
log_info "See: scripts/troubleshooting/upload-ubuntu-iso.sh"
return 1
fi
}
# Step 2: Attempt infrastructure setup
attempt_infrastructure() {
log_step "Step 2: Infrastructure Setup"
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Check cluster status
local cluster_status=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/cluster/status" 2>&1)
if echo "$cluster_status" | grep -q '"data"'; then
local node_count=$(echo "$cluster_status" | python3 -c "import sys, json; print(len(json.load(sys.stdin).get('data', [])))" 2>/dev/null)
if [ "$node_count" -gt 1 ]; then
log_info "✓ Cluster configured with $node_count nodes"
else
log_warn "Cluster exists but only has 1 node"
log_info "Need to join R630 to cluster (requires SSH)"
fi
else
log_warn "No cluster configured"
log_info "Cluster setup requires SSH access"
fi
# Check storage
local storage_status=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/storage" 2>&1)
local nfs_count=$(echo "$storage_status" | python3 -c "import sys, json; r=json.load(sys.stdin); nfs=[s for s in r.get('data', []) if s.get('type')=='nfs']; print(len(nfs))" 2>/dev/null)
if [ "$nfs_count" -gt 0 ]; then
log_info "✓ NFS storage configured"
else
log_warn "No NFS storage configured"
log_info "NFS setup requires SSH access or NFS server available"
fi
}
# Step 3: Monitor and retry VM connectivity
monitor_vms() {
log_step "Step 3: Monitoring VM Status"
local vms=(
"100 192.168.1.60 cloudflare-tunnel"
"101 192.168.1.188 k3s-master"
"102 192.168.1.121 git-server"
"103 192.168.1.82 observability"
)
log_info "Checking VM connectivity (will retry multiple times)..."
for attempt in {1..3}; do
log_info "Attempt $attempt/3:"
local any_reachable=false
for vm_spec in "${vms[@]}"; do
read -r vmid ip name <<< "$vm_spec"
if ping -c 1 -W 2 "$ip" &>/dev/null; then
log_info "$name ($ip) is reachable!"
any_reachable=true
fi
done
if [ "$any_reachable" = true ]; then
log_info "Some VMs are now reachable!"
break
fi
if [ $attempt -lt 3 ]; then
log_warn "VMs not reachable yet, waiting 30 seconds..."
sleep 30
fi
done
}
main() {
log_info "Completing All Steps with Workarounds"
echo ""
# Setup template ISO
setup_template_iso
# Infrastructure
attempt_infrastructure
# Monitor VMs
monitor_vms
log_step "Summary"
log_info "All automated steps attempted"
log_warn "Template OS installation requires manual step via Web UI"
log_info "See TROUBLESHOOTING_AND_FIXES.md for template fix instructions"
}
main "$@"

View File

@@ -0,0 +1,141 @@
#!/bin/bash
# Complete Cloudflare Tunnel Setup for VM 100
# Run this AFTER SSH access to VM 100 is working
# Usage: From root@pve: ssh ubuntu@192.168.1.244, then run this script
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
else
echo "Error: .env file not found. Please set:"
echo " CLOUDFLARE_TUNNEL_TOKEN"
echo " CLOUDFLARE_ACCOUNT_ID"
echo " CLOUDFLARE_DOMAIN"
exit 1
fi
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "========================================="
echo "Cloudflare Tunnel Configuration"
echo "========================================="
echo ""
# Create directories and user
echo -e "${GREEN}[1/6]${NC} Creating directories and user..."
sudo mkdir -p /etc/cloudflared
sudo useradd -r -s /bin/false cloudflared 2>/dev/null || true
sudo chown cloudflared:cloudflared /etc/cloudflared
echo "✓ Done"
echo ""
# Create config file
echo -e "${GREEN}[2/6]${NC} Creating config file..."
sudo tee /etc/cloudflared/config.yml > /dev/null << CONFIGEOF
tunnel: $CLOUDFLARE_TUNNEL_TOKEN
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: grafana.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:3000
- hostname: prometheus.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:9090
- hostname: git.$CLOUDFLARE_DOMAIN
service: http://192.168.1.121:3000
- hostname: proxmox-ml110.$CLOUDFLARE_DOMAIN
service: https://192.168.1.206:8006
originRequest:
noTLSVerify: true
- hostname: proxmox-r630.$CLOUDFLARE_DOMAIN
service: https://192.168.1.49:8006
originRequest:
noTLSVerify: true
- service: http_status:404
CONFIGEOF
sudo chown cloudflared:cloudflared /etc/cloudflared/config.yml
sudo chmod 600 /etc/cloudflared/config.yml
echo "✓ Done"
echo ""
# Create credentials file
echo -e "${GREEN}[3/6]${NC} Creating credentials file..."
sudo tee /etc/cloudflared/credentials.json > /dev/null << CREDEOF
{
"AccountTag": "$CLOUDFLARE_ACCOUNT_ID",
"TunnelSecret": "$CLOUDFLARE_TUNNEL_TOKEN"
}
CREDEOF
sudo chown cloudflared:cloudflared /etc/cloudflared/credentials.json
sudo chmod 600 /etc/cloudflared/credentials.json
echo "✓ Done"
echo ""
# Create systemd service
echo -e "${GREEN}[4/6]${NC} Creating systemd service..."
sudo tee /etc/systemd/system/cloudflared.service > /dev/null << SERVICEEOF
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
SERVICEEOF
echo "✓ Done"
echo ""
# Enable and start service
echo -e "${GREEN}[5/6]${NC} Enabling and starting service..."
sudo systemctl daemon-reload
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sleep 5
echo "✓ Done"
echo ""
# Verify
echo -e "${GREEN}[6/6]${NC} Verifying configuration..."
echo ""
echo "=== Service Status ==="
sudo systemctl status cloudflared --no-pager | head -15
echo ""
echo "=== Configuration Files ==="
ls -la /etc/cloudflared/
echo ""
echo "=== Recent Logs ==="
sudo journalctl -u cloudflared -n 10 --no-pager
echo ""
echo "========================================="
echo -e "${GREEN}Configuration Complete!${NC}"
echo "========================================="
echo ""
echo "Next steps:"
echo "1. Verify service: systemctl status cloudflared"
echo "2. View logs: journalctl -u cloudflared -f"
echo "3. Configure DNS records in Cloudflare Dashboard"
echo ""

View File

@@ -0,0 +1,184 @@
#!/bin/bash
source ~/.bashrc
# Complete Deployment Automation Script
# Orchestrates all deployment tasks
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check VM connectivity
check_vm_connectivity() {
local ip=$1
local name=$2
log_info "Checking connectivity to $name ($ip)..."
if ping -c 1 -W 2 "$ip" >/dev/null 2>&1; then
log_info "$name is reachable"
return 0
else
log_warn "$name is not reachable (may still be installing OS)"
return 1
fi
}
# Main deployment flow
main() {
log_header "Complete Deployment Automation"
echo ""
log_step "Phase 1: Prerequisites Check"
echo ""
# Check Proxmox connections
log_info "Verifying Proxmox connections..."
if ./scripts/utils/test-proxmox-connection.sh > /dev/null 2>&1; then
log_info "✓ Proxmox connections verified"
else
log_error "Proxmox connection failed"
exit 1
fi
echo ""
log_step "Phase 2: VM Creation Status"
echo ""
log_warn "VM creation requires manual steps via Proxmox Web UI"
log_info "Run: ./scripts/create-all-vms.sh to see available resources"
log_info "Then create VMs at: https://192.168.1.206:8006"
echo ""
# VM IPs
declare -A VM_IPS=(
["cloudflare-tunnel"]="192.168.1.60"
["k3s-master"]="192.168.1.188"
["git-server"]="192.168.1.121"
["observability"]="192.168.1.82"
)
log_info "Checking VM connectivity..."
for vm_name in "${!VM_IPS[@]}"; do
check_vm_connectivity "${VM_IPS[$vm_name]}" "$vm_name"
done
echo ""
log_step "Phase 3: Post-VM-Creation Automation"
echo ""
log_info "Once VMs are created and OS is installed, run:"
echo ""
echo " For Cloudflare Tunnel VM:"
echo " ssh user@192.168.1.60"
echo " sudo bash <(curl -s https://raw.githubusercontent.com/your-repo/scripts/setup-cloudflare-tunnel.sh)"
echo " # Or copy scripts/setup-cloudflare-tunnel.sh to VM"
echo ""
echo " For K3s VM:"
echo " ssh user@192.168.1.188"
echo " sudo bash <(curl -s https://raw.githubusercontent.com/your-repo/scripts/setup-k3s.sh)"
echo " # Or copy scripts/setup-k3s.sh to VM"
echo ""
log_step "Phase 4: Generate Setup Packages"
echo ""
# Create setup package for each VM
mkdir -p /tmp/vm-setup-packages
log_info "Creating setup packages..."
# Cloudflare Tunnel setup package
cat > /tmp/vm-setup-packages/cloudflare-tunnel-setup.sh <<'EOFTUNNEL'
#!/bin/bash
# Cloudflare Tunnel VM Setup
# Run this on the Cloudflare Tunnel VM after OS installation
set -e
cd /tmp
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
useradd -r -s /bin/false cloudflared || true
mkdir -p /etc/cloudflared
chown cloudflared:cloudflared /etc/cloudflared
echo "cloudflared installed. Next steps:"
echo "1. Run: cloudflared tunnel login"
echo "2. Run: cloudflared tunnel create azure-stack-hci"
echo "3. Configure /etc/cloudflared/config.yml"
echo "4. Set up systemd service"
EOFTUNNEL
# K3s setup package
cat > /tmp/vm-setup-packages/k3s-setup.sh <<'EOFK3S'
#!/bin/bash
# K3s Setup
# Run this on the K3s VM after OS installation
set -e
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--write-kubeconfig-mode 644" sh -
systemctl status k3s
echo "K3s installed. Next steps:"
echo "1. Configure kubectl: export KUBECONFIG=/etc/rancher/k3s/k3s.yaml"
echo "2. Verify: kubectl get nodes"
EOFK3S
chmod +x /tmp/vm-setup-packages/*.sh
log_info "✓ Setup packages created in /tmp/vm-setup-packages/"
echo ""
log_step "Phase 5: Documentation"
echo ""
log_info "All documentation is ready:"
echo " - CREATE_VMS.md - VM creation guide"
echo " - QUICK_START.md - Quick reference"
echo " - DEPLOYMENT_WITHOUT_AZURE.md - Full plan"
echo " - DEPLOYMENT_CHECKLIST.md - Progress tracker"
echo ""
log_header "Deployment Automation Complete"
echo ""
log_info "Next Steps:"
echo " 1. Create VMs via Proxmox Web UI (see CREATE_VMS.md)"
echo " 2. Install OS on each VM"
echo " 3. Copy setup scripts to VMs and run them"
echo " 4. Follow DEPLOYMENT_CHECKLIST.md to track progress"
echo ""
}
main "$@"

View File

@@ -0,0 +1,162 @@
#!/bin/bash
source ~/.bashrc
# Configure All Services on VMs
# Run this script after VMs have booted and are accessible via SSH
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# VM IP addresses
CLOUDFLARE_IP="192.168.1.60"
K3S_IP="192.168.1.188"
GIT_IP="192.168.1.121"
OBSERVABILITY_IP="192.168.1.82"
# SSH user (default for Ubuntu cloud images)
SSH_USER="${SSH_USER:-ubuntu}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
execute_remote() {
local host=$1
local command=$2
local description=$3
log_info "$description on $host"
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$SSH_USER@$host" "$command"; then
log_info "$description completed on $host"
return 0
else
log_error "$description failed on $host"
return 1
fi
}
copy_file_remote() {
local host=$1
local source=$2
local dest=$3
log_info "Copying $source to $SSH_USER@$host:$dest"
scp -o StrictHostKeyChecking=no "$source" "$SSH_USER@$host:$dest"
}
# Configure Cloudflare Tunnel
configure_cloudflare() {
log_step "Configuring Cloudflare Tunnel on VM 100"
execute_remote "$CLOUDFLARE_IP" \
"curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared && chmod +x /usr/local/bin/cloudflared" \
"Install cloudflared"
log_warn "Cloudflare Tunnel authentication requires manual steps:"
log_warn " 1. SSH to $CLOUDFLARE_IP"
log_warn " 2. Run: cloudflared tunnel login"
log_warn " 3. Create tunnel: cloudflared tunnel create azure-stack-hci"
log_warn " 4. Configure routes and systemd service"
}
# Configure K3s
configure_k3s() {
log_step "Configuring K3s on VM 101"
execute_remote "$K3S_IP" \
"curl -sfL https://get.k3s.io | sh -" \
"Install K3s"
execute_remote "$K3S_IP" \
"kubectl get nodes" \
"Verify K3s installation"
log_info "K3s kubeconfig location: /etc/rancher/k3s/k3s.yaml"
}
# Configure Git Server
configure_git() {
log_step "Configuring Git Server on VM 102"
# Check if setup script exists
if [ -f "$PROJECT_ROOT/infrastructure/gitops/gitea-deploy.sh" ]; then
copy_file_remote "$GIT_IP" \
"$PROJECT_ROOT/infrastructure/gitops/gitea-deploy.sh" \
"/tmp/gitea-deploy.sh"
execute_remote "$GIT_IP" \
"chmod +x /tmp/gitea-deploy.sh && sudo /tmp/gitea-deploy.sh" \
"Deploy Gitea"
else
log_warn "Gitea deployment script not found, manual installation required"
fi
}
# Configure Observability
configure_observability() {
log_step "Configuring Observability Stack on VM 103"
# Install Prometheus
execute_remote "$OBSERVABILITY_IP" \
"sudo apt-get update && sudo apt-get install -y prometheus" \
"Install Prometheus"
# Install Grafana
execute_remote "$OBSERVABILITY_IP" \
"sudo apt-get install -y apt-transport-https software-properties-common wget && wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add - && echo 'deb https://packages.grafana.com/oss/deb stable main' | sudo tee -a /etc/apt/sources.list.d/grafana.list && sudo apt-get update && sudo apt-get install -y grafana && sudo systemctl enable grafana-server && sudo systemctl start grafana-server" \
"Install Grafana"
log_info "Grafana should be accessible at http://$OBSERVABILITY_IP:3000"
log_info "Default credentials: admin/admin"
}
main() {
log_info "Configuring all services on VMs"
log_warn "This script requires SSH access to all VMs"
log_warn "Ensure VMs have booted and are accessible"
# Test connectivity
log_info "Testing VM connectivity..."
for ip in "$CLOUDFLARE_IP" "$K3S_IP" "$GIT_IP" "$OBSERVABILITY_IP"; do
if ! ping -c 1 -W 2 "$ip" &> /dev/null; then
log_error "Cannot reach $ip - VM may not be ready"
log_warn "Wait for VMs to fully boot and try again"
exit 1
fi
done
log_info "All VMs are reachable"
# Configure services
configure_cloudflare
configure_k3s
configure_git
configure_observability
log_info "Service configuration completed!"
log_warn "Some services may require additional manual configuration"
}
main "$@"

View File

@@ -0,0 +1,244 @@
#!/bin/bash
source ~/.bashrc
# Configure Cloudflare Tunnel Authentication and Setup on VM 100
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
VM_USER="${VM_USER:-ubuntu}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
VMID=100
VM_NAME="cloudflare-tunnel"
TUNNEL_NAME="${CLOUDFLARE_TUNNEL_NAME:-azure-stack-hci}"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
log_error "Helper library not found"
exit 1
fi
main() {
log_info "Configuring Cloudflare Tunnel on VM $VMID ($VM_NAME)"
echo ""
# Get IP using guest agent
local ip
ip="$(get_vm_ip_or_warn "$VMID" "$VM_NAME" || true)"
if [[ -z "$ip" ]]; then
log_error "Cannot get IP for VM $VMID. Ensure SSH is working and QEMU Guest Agent is installed."
exit 1
fi
log_info "Using IP: $ip"
echo ""
# Check if cloudflared is installed
log_info "Checking cloudflared installation..."
if ! ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "command -v cloudflared" &>/dev/null; then
log_warn "cloudflared not found. Installing..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /tmp/cloudflared
sudo mv /tmp/cloudflared /usr/local/bin/cloudflared
sudo chmod +x /usr/local/bin/cloudflared
cloudflared --version
EOF
log_info "cloudflared installed"
else
log_info "cloudflared is installed"
fi
# Create cloudflared user and directories
log_info "Setting up cloudflared user and directories..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
sudo useradd -r -s /bin/false cloudflared 2>/dev/null || true
sudo mkdir -p /etc/cloudflared
sudo chown cloudflared:cloudflared /etc/cloudflared
EOF
# Authenticate cloudflared (interactive)
log_info "Authenticating with Cloudflare..."
log_warn "This requires interactive browser authentication."
log_info "A browser window will open for authentication."
echo ""
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no -t "${VM_USER}@${ip}" <<EOF
set -e
cd /tmp
cloudflared tunnel login
EOF
# Create tunnel
log_info "Creating tunnel: $TUNNEL_NAME..."
local tunnel_id
tunnel_id=$(ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "cloudflared tunnel create $TUNNEL_NAME 2>&1 | grep -oP '(?<=Created tunnel )[a-f0-9-]+' || cloudflared tunnel list | grep '$TUNNEL_NAME' | awk '{print \$1}'" || true)
if [[ -z "$tunnel_id" ]]; then
log_error "Failed to create or find tunnel. Please check Cloudflare dashboard."
exit 1
fi
log_info "Tunnel ID: $tunnel_id"
# Get service IPs
local git_ip prometheus_ip grafana_ip proxmox_ml110_ip proxmox_r630_ip
git_ip="192.168.1.121" # VM 102
prometheus_ip="192.168.1.82" # VM 103
grafana_ip="192.168.1.82" # VM 103
proxmox_ml110_ip="192.168.1.206"
proxmox_r630_ip="192.168.1.49"
# Create tunnel configuration
log_info "Creating tunnel configuration..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "sudo tee /etc/cloudflared/config.yml" <<EOF
tunnel: $tunnel_id
credentials-file: /etc/cloudflared/$tunnel_id.json
ingress:
# Grafana Dashboard
- hostname: grafana.yourdomain.com
service: http://$grafana_ip:3000
originRequest:
noHappyEyeballs: true
tcpKeepAlive: 30
# Prometheus
- hostname: prometheus.yourdomain.com
service: http://$prometheus_ip:9090
originRequest:
noHappyEyeballs: true
tcpKeepAlive: 30
# Git Server (Gitea)
- hostname: git.yourdomain.com
service: http://$git_ip:3000
originRequest:
noHappyEyeballs: true
tcpKeepAlive: 30
# Proxmox ML110
- hostname: proxmox-ml110.yourdomain.com
service: https://$proxmox_ml110_ip:8006
originRequest:
noHappyEyeballs: true
tcpKeepAlive: 30
connectTimeout: 10s
tlsTimeout: 10s
httpHostHeader: proxmox-ml110.yourdomain.com
# Proxmox R630
- hostname: proxmox-r630.yourdomain.com
service: https://$proxmox_r630_ip:8006
originRequest:
noHappyEyeballs: true
tcpKeepAlive: 30
connectTimeout: 10s
tlsTimeout: 10s
httpHostHeader: proxmox-r630.yourdomain.com
# Catch-all (must be last)
- service: http_status:404
EOF
# Move credentials file to proper location
log_info "Setting up credentials file..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<EOF
set -e
if [ -f ~/.cloudflared/$tunnel_id.json ]; then
sudo mv ~/.cloudflared/$tunnel_id.json /etc/cloudflared/$tunnel_id.json
sudo chown cloudflared:cloudflared /etc/cloudflared/$tunnel_id.json
sudo chmod 600 /etc/cloudflared/$tunnel_id.json
fi
EOF
# Create systemd service
log_info "Creating systemd service..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "sudo tee /etc/systemd/system/cloudflared.service" <<'EOF'
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
# Enable and start service
log_info "Enabling and starting cloudflared service..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
sudo systemctl daemon-reload
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sleep 3
sudo systemctl status cloudflared --no-pager || true
EOF
# Verify service
log_info "Verifying tunnel status..."
sleep 5
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "sudo systemctl is-active --quiet cloudflared"; then
log_info "✓ Cloudflare Tunnel is running!"
echo ""
log_info "Tunnel Configuration:"
log_info " Tunnel Name: $TUNNEL_NAME"
log_info " Tunnel ID: $tunnel_id"
log_info " Config: /etc/cloudflared/config.yml"
echo ""
log_warn "Next steps:"
log_info "1. Configure DNS records in Cloudflare Dashboard:"
log_info " - grafana.yourdomain.com → CNAME to $tunnel_id.cfargotunnel.com"
log_info " - prometheus.yourdomain.com → CNAME to $tunnel_id.cfargotunnel.com"
log_info " - git.yourdomain.com → CNAME to $tunnel_id.cfargotunnel.com"
log_info " - proxmox-ml110.yourdomain.com → CNAME to $tunnel_id.cfargotunnel.com"
log_info " - proxmox-r630.yourdomain.com → CNAME to $tunnel_id.cfargotunnel.com"
echo ""
log_info "2. Configure Zero Trust policies in Cloudflare Dashboard"
log_info "3. View logs: ssh ${VM_USER}@${ip} 'sudo journalctl -u cloudflared -f'"
else
log_error "Tunnel service failed to start. Check logs:"
log_info " ssh ${VM_USER}@${ip} 'sudo journalctl -u cloudflared'"
exit 1
fi
}
main "$@"

View File

@@ -0,0 +1,135 @@
#!/bin/bash
# Configure Cloudflare Tunnel on VM 100
# Run this script AFTER SSH'ing to VM 100 (192.168.1.244)
# Usage: From root@pve: ssh ubuntu@192.168.1.244, then run this script
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
else
echo "Error: .env file not found. Please set these variables:"
echo " CLOUDFLARE_TUNNEL_TOKEN"
echo " CLOUDFLARE_ACCOUNT_ID"
echo " CLOUDFLARE_DOMAIN"
exit 1
fi
echo "========================================="
echo "Cloudflare Tunnel Configuration"
echo "========================================="
echo ""
# Create directories and user
echo "Creating directories and user..."
sudo mkdir -p /etc/cloudflared
sudo useradd -r -s /bin/false cloudflared 2>/dev/null || true
sudo chown cloudflared:cloudflared /etc/cloudflared
echo "✓ Directories and user created"
echo ""
# Create config file
echo "Creating config file..."
sudo tee /etc/cloudflared/config.yml > /dev/null << CONFIGEOF
tunnel: $CLOUDFLARE_TUNNEL_TOKEN
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: grafana.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:3000
- hostname: prometheus.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:9090
- hostname: git.$CLOUDFLARE_DOMAIN
service: http://192.168.1.121:3000
- hostname: proxmox-ml110.$CLOUDFLARE_DOMAIN
service: https://192.168.1.206:8006
originRequest:
noTLSVerify: true
- hostname: proxmox-r630.$CLOUDFLARE_DOMAIN
service: https://192.168.1.49:8006
originRequest:
noTLSVerify: true
- service: http_status:404
CONFIGEOF
sudo chown cloudflared:cloudflared /etc/cloudflared/config.yml
sudo chmod 600 /etc/cloudflared/config.yml
echo "✓ Config file created"
echo ""
# Create credentials file
echo "Creating credentials file..."
sudo tee /etc/cloudflared/credentials.json > /dev/null << CREDEOF
{
"AccountTag": "$CLOUDFLARE_ACCOUNT_ID",
"TunnelSecret": "$CLOUDFLARE_TUNNEL_TOKEN"
}
CREDEOF
sudo chown cloudflared:cloudflared /etc/cloudflared/credentials.json
sudo chmod 600 /etc/cloudflared/credentials.json
echo "✓ Credentials file created"
echo ""
# Create systemd service
echo "Creating systemd service..."
sudo tee /etc/systemd/system/cloudflared.service > /dev/null << SERVICEEOF
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
SERVICEEOF
echo "✓ Service file created"
echo ""
# Enable and start service
echo "Enabling and starting service..."
sudo systemctl daemon-reload
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sleep 3
echo ""
echo "========================================="
echo "Configuration Complete"
echo "========================================="
echo ""
# Check status
echo "Service Status:"
sudo systemctl status cloudflared --no-pager | head -15
echo ""
echo "Files created:"
ls -la /etc/cloudflared/
echo ""
echo "Recent logs:"
sudo journalctl -u cloudflared -n 10 --no-pager
echo ""
echo "========================================="
echo "Next Steps:"
echo "1. Verify service is running: systemctl status cloudflared"
echo "2. View logs: journalctl -u cloudflared -f"
echo "3. Configure DNS records in Cloudflare Dashboard"
echo "========================================="

View File

@@ -0,0 +1,233 @@
#!/bin/bash
# Configure Cloudflare Tunnel on VM 100
# Run this script from Proxmox host (root@pve)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
else
echo "Error: .env file not found at $PROJECT_ROOT/.env"
exit 1
fi
VMID=100
VM_USER="ubuntu"
VM_IP="192.168.1.60"
echo "========================================="
echo "Cloudflare Tunnel Configuration for VM 100"
echo "========================================="
echo ""
# Check if we can SSH to VM
echo "Checking SSH access to VM 100..."
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$VM_USER@$VM_IP" "echo 'SSH OK'" 2>/dev/null; then
echo "✓ SSH access available"
USE_SSH=true
else
echo "✗ SSH access not available"
echo " You'll need to access VM 100 via Proxmox Console"
USE_SSH=false
fi
echo ""
echo "Configuration will be prepared for:"
echo " Domain: $CLOUDFLARE_DOMAIN"
echo " Account ID: $CLOUDFLARE_ACCOUNT_ID"
echo ""
if [ "$USE_SSH" = true ]; then
echo "Configuring via SSH..."
# Create directories and user
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" <<EOF
sudo mkdir -p /etc/cloudflared
sudo useradd -r -s /bin/false cloudflared 2>/dev/null || true
sudo chown cloudflared:cloudflared /etc/cloudflared
EOF
# Create config file
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" "sudo tee /etc/cloudflared/config.yml > /dev/null" <<CONFIGEOF
tunnel: $CLOUDFLARE_TUNNEL_TOKEN
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: grafana.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:3000
- hostname: prometheus.$CLOUDFLARE_DOMAIN
service: http://192.168.1.82:9090
- hostname: git.$CLOUDFLARE_DOMAIN
service: http://192.168.1.121:3000
- hostname: proxmox-ml110.$CLOUDFLARE_DOMAIN
service: https://192.168.1.206:8006
originRequest:
noTLSVerify: true
- hostname: proxmox-r630.$CLOUDFLARE_DOMAIN
service: https://192.168.1.49:8006
originRequest:
noTLSVerify: true
- service: http_status:404
CONFIGEOF
# Create credentials file
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" "sudo tee /etc/cloudflared/credentials.json > /dev/null" <<CREDEOF
{
"AccountTag": "$CLOUDFLARE_ACCOUNT_ID",
"TunnelSecret": "$CLOUDFLARE_TUNNEL_TOKEN"
}
CREDEOF
# Set permissions
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" <<EOF
sudo chown cloudflared:cloudflared /etc/cloudflared/config.yml /etc/cloudflared/credentials.json
sudo chmod 600 /etc/cloudflared/config.yml /etc/cloudflared/credentials.json
EOF
# Create systemd service
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" "sudo tee /etc/systemd/system/cloudflared.service > /dev/null" <<SERVICEEOF
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
SERVICEEOF
# Enable and start service
ssh -o StrictHostKeyChecking=no "$VM_USER@$VM_IP" <<EOF
sudo systemctl daemon-reload
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sleep 3
sudo systemctl status cloudflared --no-pager
EOF
echo ""
echo "✓ Configuration complete via SSH"
else
echo ""
echo "========================================="
echo "Manual Configuration Required"
echo "========================================="
echo ""
echo "Since SSH is not available, please:"
echo ""
echo "1. Access VM 100 via Proxmox Console:"
echo " - Go to: https://192.168.1.206:8006"
echo " - Navigate to: VM 100 → Console"
echo " - Login as: ubuntu"
echo ""
echo "2. Run these commands on VM 100:"
echo ""
cat <<'MANUAL'
# Create directories and user
sudo mkdir -p /etc/cloudflared
sudo useradd -r -s /bin/false cloudflared 2>/dev/null || true
sudo chown cloudflared:cloudflared /etc/cloudflared
# Create config file
sudo tee /etc/cloudflared/config.yml > /dev/null << 'CONFIGEOF'
tunnel: CLOUDFLARE_TUNNEL_TOKEN
credentials-file: /etc/cloudflared/credentials.json
ingress:
- hostname: grafana.CLOUDFLARE_DOMAIN
service: http://192.168.1.82:3000
- hostname: prometheus.CLOUDFLARE_DOMAIN
service: http://192.168.1.82:9090
- hostname: git.CLOUDFLARE_DOMAIN
service: http://192.168.1.121:3000
- hostname: proxmox-ml110.CLOUDFLARE_DOMAIN
service: https://192.168.1.206:8006
originRequest:
noTLSVerify: true
- hostname: proxmox-r630.CLOUDFLARE_DOMAIN
service: https://192.168.1.49:8006
originRequest:
noTLSVerify: true
- service: http_status:404
CONFIGEOF
# Replace placeholders (run these with actual values from .env)
sudo sed -i "s/CLOUDFLARE_TUNNEL_TOKEN/$CLOUDFLARE_TUNNEL_TOKEN/g" /etc/cloudflared/config.yml
sudo sed -i "s/CLOUDFLARE_DOMAIN/$CLOUDFLARE_DOMAIN/g" /etc/cloudflared/config.yml
# Create credentials file
sudo tee /etc/cloudflared/credentials.json > /dev/null << CREDEOF
{
"AccountTag": "CLOUDFLARE_ACCOUNT_ID",
"TunnelSecret": "CLOUDFLARE_TUNNEL_TOKEN"
}
CREDEOF
# Replace placeholders
sudo sed -i "s/CLOUDFLARE_ACCOUNT_ID/$CLOUDFLARE_ACCOUNT_ID/g" /etc/cloudflared/credentials.json
sudo sed -i "s/CLOUDFLARE_TUNNEL_TOKEN/$CLOUDFLARE_TUNNEL_TOKEN/g" /etc/cloudflared/credentials.json
# Set permissions
sudo chown cloudflared:cloudflared /etc/cloudflared/config.yml /etc/cloudflared/credentials.json
sudo chmod 600 /etc/cloudflared/config.yml /etc/cloudflared/credentials.json
# Create systemd service
sudo tee /etc/systemd/system/cloudflared.service > /dev/null << 'SERVICEEOF'
[Unit]
Description=Cloudflare Tunnel
After=network.target
[Service]
Type=simple
User=cloudflared
ExecStart=/usr/local/bin/cloudflared tunnel --config /etc/cloudflared/config.yml run
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
SERVICEEOF
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
systemctl status cloudflared
MANUAL
echo ""
echo "Note: Replace CLOUDFLARE_TUNNEL_TOKEN, CLOUDFLARE_DOMAIN, and CLOUDFLARE_ACCOUNT_ID"
echo " with actual values from your .env file"
echo ""
echo "Or source the .env file first:"
echo " source /path/to/.env"
echo ""
fi
echo ""
echo "========================================="
echo "Configuration Complete"
echo "========================================="
echo ""
echo "Next steps:"
echo "1. Verify service: systemctl status cloudflared"
echo "2. View logs: journalctl -u cloudflared -f"
echo "3. Configure DNS records in Cloudflare Dashboard"
echo ""

View File

@@ -0,0 +1,230 @@
#!/bin/bash
source ~/.bashrc
# Configure GitOps Workflows (Flux) on K3s Cluster
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
VM_USER="${VM_USER:-ubuntu}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
VMID=101
VM_NAME="k3s-master"
GIT_REPO="${GIT_REPO:-http://192.168.1.121:3000/hc-stack/gitops.git}"
GIT_BRANCH="${GIT_BRANCH:-main}"
GIT_PATH="${GIT_PATH:-gitops/}"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
log_error "Helper library not found"
exit 1
fi
main() {
log_info "Configuring GitOps Workflows on VM $VMID ($VM_NAME)"
echo ""
# Get IP using guest agent
local ip
ip="$(get_vm_ip_or_warn "$VMID" "$VM_NAME" || true)"
if [[ -z "$ip" ]]; then
log_error "Cannot get IP for VM $VMID. Ensure SSH is working and QEMU Guest Agent is installed."
exit 1
fi
log_info "Using IP: $ip"
echo ""
# Check K3s installation
log_info "Checking K3s installation..."
if ! ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "sudo kubectl version --client" &>/dev/null; then
log_error "K3s/kubectl not found. Please install K3s first."
exit 1
fi
log_info "K3s is installed"
# Install Flux CLI
log_info "Installing Flux CLI..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
if ! command -v flux &>/dev/null; then
curl -s https://fluxcd.io/install.sh | sudo bash
flux --version
else
echo "Flux CLI already installed"
flux --version
fi
EOF
# Check if Flux is already installed
log_info "Checking if Flux is already installed..."
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "sudo kubectl get namespace flux-system" &>/dev/null; then
log_warn "Flux is already installed. Skipping installation."
else
# Install Flux
log_info "Installing Flux in K3s cluster..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
sudo flux install --components=source-controller,kustomize-controller,helm-controller,notification-controller
EOF
log_info "Waiting for Flux to be ready..."
sleep 10
fi
# Create Git repository secret (if using HTTPS with token)
log_info "Configuring Git repository access..."
log_warn "Note: For Gitea, you may need to create a token and configure authentication"
# For now, we'll set up a basic GitRepository source
# User will need to configure authentication based on their setup
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<EOF
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
# Create namespace for applications if it doesn't exist
sudo kubectl create namespace blockchain --dry-run=client -o yaml | sudo kubectl apply -f -
sudo kubectl create namespace monitoring --dry-run=client -o yaml | sudo kubectl apply -f -
sudo kubectl create namespace hc-stack --dry-run=client -o yaml | sudo kubectl apply -f -
# Create GitRepository source
cat <<'GITREPO' | sudo kubectl apply -f -
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: gitops-repo
namespace: flux-system
spec:
interval: 1m
url: $GIT_REPO
ref:
branch: $GIT_BRANCH
ignore: |
# Exclude certain paths
.git/
.github/
docs/
scripts/
GITREPO
EOF
log_info "GitRepository source created"
log_warn "If your Git repository requires authentication, you'll need to:"
log_info "1. Create a Git token in Gitea"
log_info "2. Create a secret: kubectl create secret generic gitops-repo-auth \\"
log_info " --from-literal=username=<username> \\"
log_info " --from-literal=password=<token> \\"
log_info " -n flux-system"
log_info "3. Update GitRepository to reference the secret"
echo ""
# Create Kustomization for infrastructure
log_info "Creating Kustomization for infrastructure..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
cat <<'KUSTOMIZATION' | sudo kubectl apply -f -
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infrastructure
namespace: flux-system
spec:
interval: 5m
path: ./gitops/infrastructure
prune: true
sourceRef:
kind: GitRepository
name: gitops-repo
validation: client
KUSTOMIZATION
EOF
# Create Kustomization for applications
log_info "Creating Kustomization for applications..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
cat <<'KUSTOMIZATION' | sudo kubectl apply -f -
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: applications
namespace: flux-system
spec:
interval: 5m
path: ./gitops/apps
prune: true
sourceRef:
kind: GitRepository
name: gitops-repo
validation: client
KUSTOMIZATION
EOF
# Wait for reconciliation
log_info "Waiting for Flux to reconcile..."
sleep 10
# Check Flux status
log_info "Checking Flux status..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
echo "=== Flux Components ==="
sudo kubectl get pods -n flux-system
echo ""
echo "=== GitRepository Status ==="
sudo kubectl get gitrepository -n flux-system
echo ""
echo "=== Kustomization Status ==="
sudo kubectl get kustomization -n flux-system
EOF
log_info "✓ GitOps workflows configured!"
echo ""
log_info "Next steps:"
log_info "1. Ensure your Git repository is accessible from the cluster"
log_info "2. Configure authentication if required (see warnings above)"
log_info "3. Push your GitOps manifests to: $GIT_REPO"
log_info "4. Monitor reconciliation: kubectl get kustomization -n flux-system"
log_info "5. View logs: kubectl logs -n flux-system -l app=kustomize-controller"
}
main "$@"

View File

@@ -0,0 +1,154 @@
#!/bin/bash
source ~/.bashrc
# Configure Cloud-Init on Proxmox VMs via API
# Sets up IP addresses, users, and basic configuration
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
configure_vm_cloudinit() {
local vmid=$1
local name=$2
local ip=$3
local gateway=$4
local user=$5
log_info "Configuring cloud-init for VM $vmid ($name)..."
local tokens=$(get_api_token)
if [ -z "$tokens" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Configure cloud-init settings
local response=$(curl -s -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "ipconfig0=ip=$ip/24,gw=$gateway" \
-d "ciuser=$user" \
-d "cipassword=" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$response" | grep -q '"data"'; then
log_info "VM $vmid cloud-init configured successfully"
return 0
else
log_error "Failed to configure VM $vmid: $response"
return 1
fi
}
start_vm() {
local vmid=$1
local name=$2
log_info "Starting VM $vmid ($name)..."
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
local response=$(curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start" 2>&1)
if echo "$response" | grep -q '"data"'; then
log_info "VM $vmid started successfully"
return 0
else
log_warn "VM $vmid may already be running or start failed: $response"
return 0
fi
}
main() {
log_info "Configuring cloud-init on all service VMs"
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
# VM definitions: vmid name ip gateway user
local vms=(
"100 cloudflare-tunnel 192.168.1.60 192.168.1.254 ubuntu"
"101 k3s-master 192.168.1.188 192.168.1.254 ubuntu"
"102 git-server 192.168.1.121 192.168.1.254 ubuntu"
"103 observability 192.168.1.82 192.168.1.254 ubuntu"
)
# Configure cloud-init
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway user <<< "$vm_spec"
configure_vm_cloudinit "$vmid" "$name" "$ip" "$gateway" "$user"
sleep 1
done
log_info "Waiting 5 seconds before starting VMs..."
sleep 5
# Start VMs
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway user <<< "$vm_spec"
start_vm "$vmid" "$name"
sleep 2
done
log_info "Cloud-init configuration and VM startup completed!"
log_warn "VMs are starting. They will boot with cloud-init configuration."
log_warn "Check VM status via Proxmox web UI or API."
}
main "$@"

View File

@@ -0,0 +1,200 @@
#!/bin/bash
source ~/.bashrc
# Configure Services on VMs
# Sets up Cloudflare Tunnel, K3s, Git Server, and Observability
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
SSH_KEY="$HOME/.ssh/id_ed25519_proxmox"
VM_USER="ubuntu"
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
log_error "Helper library not found. Run this script on Proxmox host or via SSH."
exit 1
fi
# VM definitions: vmid name (no IP - discovered via guest agent)
VMS=(
"100 cloudflare-tunnel"
"101 k3s-master"
"102 git-server"
"103 observability"
)
wait_for_vm() {
local vmid=$1
local name=$2
local max_wait=300
local waited=0
log_info "Waiting for $name (VM $vmid) to be reachable..."
# Ensure guest agent is enabled
ensure_guest_agent_enabled "$vmid" || true
while [ $waited -lt $max_wait ]; do
local ip
ip="$(get_vm_ip_from_guest_agent "$vmid" || true)"
if [[ -n "$ip" ]]; then
log_info "$name is reachable at $ip"
sleep 10 # Give it a bit more time for SSH
if timeout 3 bash -c "cat < /dev/null > /dev/tcp/$ip/22" 2>/dev/null; then
log_info "✓ SSH is available"
return 0
fi
fi
sleep 5
waited=$((waited + 5))
echo -n "."
done
echo ""
log_warn "$name (VM $vmid) not reachable after $max_wait seconds"
return 1
}
configure_cloudflare_tunnel() {
local ip=$1
log_step "Configuring Cloudflare Tunnel on VM 100"
log_info "Installing cloudflared..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$VM_USER@$ip" "sudo apt update && sudo apt install -y cloudflared" || {
log_error "Failed to install cloudflared"
return 1
}
log_warn "Cloudflare Tunnel requires authentication - manual setup needed"
log_info "See: docs/services/cloudflare-tunnel-setup.md"
}
configure_k3s() {
local ip=$1
log_step "Configuring K3s on VM 101"
log_info "Installing K3s..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$VM_USER@$ip" "curl -sfL https://get.k3s.io | sh -" || {
log_error "Failed to install K3s"
return 1
}
log_info "Verifying K3s installation..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$VM_USER@$ip" "sudo kubectl get nodes" || {
log_error "K3s not working properly"
return 1
}
log_info "✓ K3s installed and running"
}
configure_git_server() {
local ip=$1
log_step "Configuring Git Server on VM 102"
log_info "Installing Gitea..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$VM_USER@$ip" "sudo apt update && sudo apt install -y docker.io docker-compose" || {
log_error "Failed to install Docker"
return 1
}
log_warn "Gitea setup requires manual configuration"
log_info "See: docs/services/git-server-setup.md"
}
configure_observability() {
local ip=$1
log_step "Configuring Observability Stack on VM 103"
log_info "Installing Docker and Docker Compose..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "$VM_USER@$ip" "sudo apt update && sudo apt install -y docker.io docker-compose" || {
log_error "Failed to install Docker"
return 1
}
log_warn "Observability stack requires manual configuration"
log_info "See: docs/services/observability-setup.md"
}
main() {
log_info "Configuring Services on VMs"
echo ""
if [ ! -f "$SSH_KEY" ]; then
log_error "SSH key not found: $SSH_KEY"
exit 1
fi
# Wait for VMs to be accessible and get IPs
declare -A VM_IPS
for vm_spec in "${VMS[@]}"; do
read -r vmid name <<< "$vm_spec"
wait_for_vm "$vmid" "$name"
# Get IP from guest agent
local ip
ip="$(get_vm_ip_or_warn "$vmid" "$name" || true)"
if [[ -n "$ip" ]]; then
VM_IPS["$vmid"]="$ip"
else
log_error "Cannot get IP for VM $vmid ($name), skipping"
continue
fi
done
# Configure services using discovered IPs
if [[ -n "${VM_IPS[100]:-}" ]]; then
configure_cloudflare_tunnel "${VM_IPS[100]}"
fi
if [[ -n "${VM_IPS[101]:-}" ]]; then
configure_k3s "${VM_IPS[101]}"
fi
if [[ -n "${VM_IPS[102]:-}" ]]; then
configure_git_server "${VM_IPS[102]}"
fi
if [[ -n "${VM_IPS[103]:-}" ]]; then
configure_observability "${VM_IPS[103]}"
fi
log_step "Service Configuration Complete!"
log_info "Some services require manual configuration (see docs/services/)"
}
main "$@"

View File

@@ -0,0 +1,119 @@
#!/bin/bash
source ~/.bashrc
# Continue All Steps with Troubleshooting
# Attempts to complete all steps and troubleshoot issues
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_issue() {
echo -e "${RED}[ISSUE]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
# Step 1: Diagnose Issues
diagnose_issues() {
log_step "Step 1: Diagnosing Issues"
if [ -f "$PROJECT_ROOT/scripts/troubleshooting/diagnose-vm-issues.sh" ]; then
"$PROJECT_ROOT/scripts/troubleshooting/diagnose-vm-issues.sh"
else
log_warn "Diagnosis script not found"
fi
}
# Step 2: Fix Template (if possible)
fix_template() {
log_step "Step 2: Attempting Template Fixes"
log_info "Template disk expanded to 8G (if not already)"
log_warn "Template needs OS installation - see TROUBLESHOOTING_AND_FIXES.md"
log_info "This requires manual access to Proxmox Web UI"
}
# Step 3: Continue Infrastructure Setup
continue_infrastructure() {
log_step "Step 3: Continuing Infrastructure Setup"
log_info "Checking cluster status..."
# Cluster check done in main script
log_warn "Infrastructure setup requires SSH access to Proxmox hosts"
log_info "To configure cluster:"
log_info " ssh root@192.168.1.206"
log_info " export CLUSTER_NAME=hc-cluster NODE_ROLE=create"
log_info " ./infrastructure/proxmox/cluster-setup.sh"
}
# Step 4: Monitor VM Status
monitor_vms() {
log_step "Step 4: Monitoring VM Status"
local vms=("100" "101" "102" "103")
local all_ready=true
for vmid in "${vms[@]}"; do
# Check via API
log_info "Checking VM $vmid..."
done
if [ "$all_ready" = false ]; then
log_warn "VMs not ready - may need template OS installation"
fi
}
main() {
log_info "Continuing All Steps with Troubleshooting"
echo ""
# Diagnose
diagnose_issues
# Fix template
fix_template
# Continue infrastructure
continue_infrastructure
# Monitor
monitor_vms
log_step "Summary"
log_issue "CRITICAL: Template VM 9000 needs OS installation"
log_info "See TROUBLESHOOTING_AND_FIXES.md for detailed fix instructions"
log_info "After template is fixed, recreate VMs and continue"
}
main "$@"

View File

@@ -0,0 +1,158 @@
#!/bin/bash
source ~/.bashrc
# Complete Deployment Script - All Services
# Orchestrates deployment of all VMs and services
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# VM configurations
declare -A VMS=(
["100"]="cloudflare-tunnel:192.168.1.60:scripts/setup-cloudflare-tunnel.sh"
["101"]="k3s-master:192.168.1.188:scripts/setup-k3s.sh"
["102"]="git-server:192.168.1.121:scripts/setup-git-server.sh"
["103"]="observability:192.168.1.82:scripts/setup-observability.sh"
)
# Check VM connectivity and run setup
setup_vm() {
local vmid=$1
local name=$2
local ip=$3
local script=$4
log_step "Setting up $name ($ip)..."
# Check connectivity
if ! ping -c 1 -W 2 "$ip" >/dev/null 2>&1; then
log_warn "$name ($ip) is not reachable. Skipping..."
return 1
fi
log_info "Copying setup script to $name..."
if scp "$script" "user@$ip:/tmp/setup.sh" 2>/dev/null; then
log_info "Running setup script on $name..."
ssh "user@$ip" "sudo bash /tmp/setup.sh" || log_warn "Setup script failed on $name"
else
log_warn "Could not copy script to $name. Manual setup required."
log_info "Manual steps:"
echo " 1. SSH to $name: ssh user@$ip"
echo " 2. Copy $script to VM"
echo " 3. Run: sudo bash /path/to/script"
fi
}
main() {
log_header "Complete Deployment - All Services"
echo ""
log_step "Phase 1: Prerequisites"
echo ""
if ./scripts/utils/test-proxmox-connection.sh > /dev/null 2>&1; then
log_info "✓ Proxmox connections verified"
else
log_error "Proxmox connection failed"
exit 1
fi
echo ""
log_step "Phase 2: VM Creation Status"
echo ""
log_warn "VMs must be created via Proxmox Web UI first"
log_info "Proxmox URL: https://192.168.1.206:8006"
log_info "See CREATE_VMS.md for detailed instructions"
echo ""
log_info "Required VMs:"
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name ip script <<< "${VMS[$vmid]}"
echo " - $name (ID: $vmid, IP: $ip)"
done
echo ""
read -p "Have all VMs been created and OS installed? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_warn "Please create VMs first, then run this script again"
exit 0
fi
log_step "Phase 3: Automated Setup"
echo ""
log_info "Attempting to set up each VM..."
echo ""
for vmid in "${!VMS[@]}"; do
IFS=':' read -r name ip script <<< "${VMS[$vmid]}"
setup_vm "$vmid" "$name" "$ip" "$script"
echo ""
done
log_step "Phase 4: Post-Setup Verification"
echo ""
log_info "Verifying services..."
echo ""
# Check services
services=(
"192.168.1.60:Cloudflare Tunnel"
"192.168.1.188:6443:K3s API"
"192.168.1.121:3000:Gitea"
"192.168.1.82:9090:Prometheus"
"192.168.1.82:3000:Grafana"
)
for service in "${services[@]}"; do
IFS=':' read -r ip port name <<< "$service"
if [ -z "$port" ]; then
port="22"
fi
if timeout 2 bash -c "echo >/dev/tcp/$ip/$port" 2>/dev/null; then
log_info "$name is accessible"
else
log_warn "$name is not accessible (may still be starting)"
fi
done
log_header "Deployment Complete"
echo ""
log_info "Next steps:"
echo " 1. Configure Cloudflare Tunnel (see docs/cloudflare-integration.md)"
echo " 2. Set up K3s namespaces and deploy services"
echo " 3. Configure GitOps repository"
echo " 4. Deploy HC Stack services"
echo ""
log_info "See DEPLOYMENT_CHECKLIST.md to track remaining tasks"
}
main "$@"

180
scripts/deploy/deploy-gitea.sh Executable file
View File

@@ -0,0 +1,180 @@
#!/bin/bash
source ~/.bashrc
# Deploy Gitea on VM 102 using guest-agent IP discovery
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
VM_USER="${VM_USER:-ubuntu}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
VMID=102
VM_NAME="git-server"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
log_error "Helper library not found"
exit 1
fi
main() {
log_info "Deploying Gitea on VM $VMID ($VM_NAME)"
echo ""
# Get IP using guest agent
local ip
ip="$(get_vm_ip_or_warn "$VMID" "$VM_NAME" || true)"
if [[ -z "$ip" ]]; then
log_error "Cannot get IP for VM $VMID. Ensure SSH is working and QEMU Guest Agent is installed."
exit 1
fi
log_info "Using IP: $ip"
echo ""
# Check if Docker is installed
log_info "Checking Docker installation..."
if ! ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "command -v docker" &>/dev/null; then
log_warn "Docker not found. Installing Docker..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
sudo apt-get update -qq
sudo apt-get install -y docker.io docker-compose
sudo usermod -aG docker $USER
EOF
log_info "Docker installed. You may need to log out and back in for group changes."
else
log_info "Docker is installed"
fi
# Create Gitea directory
log_info "Setting up Gitea directory..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
mkdir -p ~/gitea
cd ~/gitea
EOF
# Copy docker-compose file
log_info "Creating docker-compose.yml..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "cat > ~/gitea/docker-compose.yml" <<EOF
version: '3.8'
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__server__DOMAIN=${ip}
- GITEA__server__SSH_DOMAIN=${ip}
- GITEA__server__SSH_PORT=2222
- GITEA__server__ROOT_URL=http://${ip}:3000
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:22"
depends_on:
- db
networks:
- gitea-network
db:
image: postgres:15
container_name: gitea-db
restart: unless-stopped
environment:
- POSTGRES_USER=gitea
- POSTGRES_PASSWORD=gitea
- POSTGRES_DB=gitea
volumes:
- gitea_db_data:/var/lib/postgresql/data
networks:
- gitea-network
volumes:
gitea_data:
driver: local
gitea_db_data:
driver: local
networks:
gitea-network:
driver: bridge
EOF
# Deploy
log_info "Deploying Gitea with Docker Compose..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
cd ~/gitea
sudo docker-compose up -d
EOF
# Wait for service to be ready
log_info "Waiting for Gitea to start..."
sleep 10
# Verify
log_info "Verifying Gitea deployment..."
local max_wait=60
local elapsed=0
while [ $elapsed -lt $max_wait ]; do
if curl -s "http://${ip}:3000" &>/dev/null; then
log_info "✓ Gitea is running!"
echo ""
log_info "Access Gitea at: http://${ip}:3000"
log_info "SSH access: ssh://git@${ip}:2222"
return 0
fi
sleep 5
elapsed=$((elapsed + 5))
echo -n "."
done
log_warn "Gitea may not be fully ready yet. Check logs with:"
log_info " ssh ${VM_USER}@${ip} 'cd ~/gitea && sudo docker-compose logs'"
}
main "$@"

View File

@@ -0,0 +1,197 @@
#!/bin/bash
source ~/.bashrc
# Deploy Observability Stack (Prometheus + Grafana) on VM 103 using guest-agent IP discovery
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
VM_USER="${VM_USER:-ubuntu}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
VMID=103
VM_NAME="observability"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
log_error "Helper library not found"
exit 1
fi
main() {
log_info "Deploying Observability Stack on VM $VMID ($VM_NAME)"
echo ""
# Get IP using guest agent
local ip
ip="$(get_vm_ip_or_warn "$VMID" "$VM_NAME" || true)"
if [[ -z "$ip" ]]; then
log_error "Cannot get IP for VM $VMID. Ensure SSH is working and QEMU Guest Agent is installed."
exit 1
fi
log_info "Using IP: $ip"
echo ""
# Check if Docker is installed
log_info "Checking Docker installation..."
if ! ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "command -v docker" &>/dev/null; then
log_warn "Docker not found. Installing Docker..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
sudo apt-get update -qq
sudo apt-get install -y docker.io docker-compose
sudo usermod -aG docker $USER
EOF
log_info "Docker installed. You may need to log out and back in for group changes."
else
log_info "Docker is installed"
fi
# Create observability directory structure
log_info "Setting up observability directory..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
mkdir -p ~/observability/prometheus
cd ~/observability
EOF
# Create Prometheus config
log_info "Creating Prometheus configuration..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "cat > ~/observability/prometheus/prometheus.yml" <<'EOF'
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
EOF
# Create docker-compose file
log_info "Creating docker-compose.yml..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" "cat > ~/observability/docker-compose.yml" <<'EOF'
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
ports:
- "9090:9090"
volumes:
- ./prometheus:/etc/prometheus
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=30d'
networks:
- observability
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=http://localhost:3000
volumes:
- grafana-data:/var/lib/grafana
networks:
- observability
depends_on:
- prometheus
volumes:
prometheus-data:
driver: local
grafana-data:
driver: local
networks:
observability:
driver: bridge
EOF
# Deploy
log_info "Deploying Observability Stack with Docker Compose..."
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "${VM_USER}@${ip}" <<'EOF'
set -e
cd ~/observability
sudo docker-compose up -d
EOF
# Wait for services to be ready
log_info "Waiting for services to start..."
sleep 15
# Verify
log_info "Verifying services..."
local prometheus_ok=false
local grafana_ok=false
for i in {1..12}; do
if curl -s "http://${ip}:9090/-/healthy" &>/dev/null; then
prometheus_ok=true
fi
if curl -s "http://${ip}:3000/api/health" &>/dev/null; then
grafana_ok=true
fi
if [ "$prometheus_ok" = true ] && [ "$grafana_ok" = true ]; then
break
fi
sleep 5
echo -n "."
done
echo ""
if [ "$prometheus_ok" = true ] && [ "$grafana_ok" = true ]; then
log_info "✓ Observability Stack is running!"
echo ""
log_info "Access services:"
log_info " Prometheus: http://${ip}:9090"
log_info " Grafana: http://${ip}:3000 (admin/admin)"
else
log_warn "Some services may not be fully ready. Check logs with:"
log_info " ssh ${VM_USER}@${ip} 'cd ~/observability && sudo docker-compose logs'"
fi
}
main "$@"

158
scripts/deploy/deploy-start.sh Executable file
View File

@@ -0,0 +1,158 @@
#!/bin/bash
source ~/.bashrc
# Start Deployment Script
# Guides through initial VM creation and setup
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
log_header() {
echo -e "${CYAN}========================================${NC}"
echo -e "${CYAN}$1${NC}"
echo -e "${CYAN}========================================${NC}"
}
# Load environment variables
if [ -f .env ]; then
set -a
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
set +a
else
log_error ".env file not found!"
log_info "Copy .env.example to .env and configure it"
exit 1
fi
log_header "Azure Stack HCI Deployment - Starting"
log_step "Step 1: Verifying Prerequisites"
echo ""
# Test Proxmox connections
log_info "Testing Proxmox connections..."
if ./scripts/utils/test-proxmox-connection.sh > /dev/null 2>&1; then
log_info "✓ Proxmox connections verified"
else
log_error "Proxmox connection failed. Please check your .env file"
exit 1
fi
echo ""
log_step "Step 2: VM Creation Options"
echo ""
log_info "You have 3 options to create VMs:"
echo ""
echo " ${CYAN}Option 1: Proxmox Web UI (Recommended for first-time)${NC}"
echo " - Access: https://192.168.1.206:8006"
echo " - Login: root@pam / (password from PVE_ROOT_PASS)"
echo " - See CREATE_VMS.md for detailed instructions"
echo ""
echo " ${CYAN}Option 2: Terraform${NC}"
echo " - Requires VM templates to be created first"
echo " - cd terraform/proxmox && terraform init && terraform apply"
echo ""
echo " ${CYAN}Option 3: Manual API (Advanced)${NC}"
echo " - Use scripts/proxmox/create-service-vms.sh"
echo ""
read -p "Which option do you want to use? (1/2/3) [1]: " choice
choice=${choice:-1}
case $choice in
1)
log_info "Opening Proxmox Web UI instructions..."
echo ""
log_warn "Please create the following VMs manually:"
echo ""
echo " 1. Cloudflare Tunnel VM"
echo " - VM ID: 100"
echo " - Name: cloudflare-tunnel"
echo " - IP: 192.168.1.60"
echo " - Specs: 2 CPU, 4GB RAM, 40GB disk"
echo ""
echo " 2. K3s Master VM"
echo " - VM ID: 101"
echo " - Name: k3s-master"
echo " - IP: 192.168.1.188"
echo " - Specs: 4 CPU, 8GB RAM, 80GB disk"
echo ""
echo " 3. Git Server VM"
echo " - VM ID: 102"
echo " - Name: git-server"
echo " - IP: 192.168.1.121"
echo " - Specs: 4 CPU, 8GB RAM, 100GB disk"
echo ""
echo " 4. Observability VM"
echo " - VM ID: 103"
echo " - Name: observability"
echo " - IP: 192.168.1.82"
echo " - Specs: 4 CPU, 8GB RAM, 200GB disk"
echo ""
log_info "Proxmox URL: https://192.168.1.206:8006"
log_info "See CREATE_VMS.md for detailed step-by-step instructions"
echo ""
read -p "Press Enter after you've created at least the Cloudflare Tunnel VM..."
;;
2)
log_info "Initializing Terraform..."
cd terraform/proxmox
if [ ! -f terraform.tfvars ]; then
log_error "terraform.tfvars not found. Please create it first."
exit 1
fi
terraform init
log_info "Review the plan:"
terraform plan
read -p "Apply Terraform? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
terraform apply
fi
cd ../..
;;
3)
log_info "Using API-based creation..."
./scripts/proxmox/create-service-vms.sh
;;
esac
echo ""
log_step "Step 3: Next Steps After VM Creation"
echo ""
log_info "After creating VMs, you need to:"
echo ""
echo " 1. Install Ubuntu 22.04 LTS on each VM"
echo " 2. Configure static IP addresses"
echo " 3. Run setup scripts:"
echo " - scripts/setup-cloudflare-tunnel.sh (on Tunnel VM)"
echo " - scripts/setup-k3s.sh (on K3s VM)"
echo ""
log_info "See QUICK_START.md for complete instructions"
echo ""
log_header "Deployment Started"
log_info "Check DEPLOYMENT_CHECKLIST.md to track progress"

View File

@@ -0,0 +1,174 @@
#!/bin/bash
source ~/.bashrc
# Deploy Service VMs via Proxmox API
# Can be executed without SSH access
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
TEMPLATE_VMID="${TEMPLATE_VMID:-9000}"
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
create_vm_from_template() {
local vmid=$1
local name=$2
local ip=$3
local gateway=$4
local cores=$5
local memory=$6
local disk_size=$7
local bridge="${8:-vmbr0}"
log_info "Creating VM $vmid: $name"
local tokens=$(get_api_token)
if [ -z "$tokens" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Check if template exists
local template_check=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_VMID/status/current" 2>&1)
if ! echo "$template_check" | grep -q '"data"'; then
log_error "Template VM $TEMPLATE_VMID not found or not accessible"
return 1
fi
# Clone VM from template
log_info "Cloning from template $TEMPLATE_VMID..."
local clone_response=$(curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "newid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_VMID/clone" 2>&1)
if echo "$clone_response" | grep -q '"data"'; then
log_info "VM cloned successfully"
else
log_error "Failed to clone VM: $clone_response"
return 1
fi
# Wait for clone to complete
sleep 5
# Configure VM
log_info "Configuring VM $vmid..."
# Set CPU and memory
curl -s -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "cores=$cores" \
-d "memory=$memory" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Set network and IP
curl -s -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "net0=virtio,bridge=$bridge" \
-d "ipconfig0=ip=$ip/24,gw=$gateway" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
log_info "VM $vmid configured successfully"
return 0
}
main() {
log_info "Deploying Service VMs via Proxmox API"
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
# VM definitions
# Format: vmid name ip gateway cores memory_mb disk_gb bridge
local vms=(
"100 cloudflare-tunnel 192.168.1.60 192.168.1.254 2 4096 40 vmbr0"
"101 k3s-master 192.168.1.188 192.168.1.254 4 8192 80 vmbr0"
"102 git-server 192.168.1.121 192.168.1.254 4 8192 100 vmbr0"
"103 observability 192.168.1.82 192.168.1.254 4 8192 200 vmbr0"
)
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway cores memory disk bridge <<< "$vm_spec"
# Check if VM already exists
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
local vm_check=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/current" 2>&1)
if echo "$vm_check" | grep -q '"data"'; then
log_warn "VM $vmid ($name) already exists, skipping"
continue
fi
create_vm_from_template "$vmid" "$name" "$ip" "$gateway" "$cores" "$memory" "$disk" "$bridge"
done
log_info "VM deployment completed!"
log_warn "Next steps:"
log_warn " 1. Install Ubuntu 24.04 on each VM via Proxmox console"
log_warn " 2. Configure services after OS installation"
}
main "$@"

View File

@@ -0,0 +1,67 @@
#!/bin/bash
source ~/.bashrc
# Quick Deployment Script - Without Azure Arc
# Deploys infrastructure stack without Azure dependencies
set -e
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "========================================="
echo "Deployment Without Azure Arc"
echo "========================================="
echo ""
echo "This script will guide you through deployment"
echo "without Azure Arc integration."
echo ""
echo "Press Enter to continue or Ctrl+C to cancel..."
read
# Load environment variables
if [ -f .env ]; then
source <(grep -v '^#' .env | grep -v '^$' | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep '=')
fi
echo ""
echo "=== Phase 1: Verify Proxmox Cluster ==="
echo "Testing Proxmox connections..."
./scripts/utils/test-proxmox-connection.sh
echo ""
echo "=== Phase 2: Create Service VMs ==="
echo "Choose deployment method:"
echo "1. Use Terraform (automated)"
echo "2. Manual via Proxmox UI"
read -p "Choice [1-2]: " vm_choice
if [ "$vm_choice" = "1" ]; then
echo "Using Terraform for VM creation..."
cd terraform/proxmox
terraform init
terraform plan
echo "Review plan above, then run: terraform apply"
else
echo "Create VMs manually via Proxmox UI:"
echo " - K3s VM: 192.168.1.188"
echo " - Cloudflare Tunnel VM: 192.168.1.60"
echo " - Git Server VM: 192.168.1.121"
echo " - Observability VM: 192.168.1.82"
fi
echo ""
echo "=== Phase 3: Cloudflare Tunnel Setup ==="
echo "Tunnel token available: ${CLOUDFLARE_TUNNEL_TOKEN:0:10}***"
echo "See DEPLOYMENT_WITHOUT_AZURE.md for detailed setup"
echo ""
echo "=== Phase 4: Kubernetes Deployment ==="
echo "Once K3s VM is ready, run:"
echo " ssh ubuntu@192.168.1.188"
echo " curl -sfL https://get.k3s.io | sh -"
echo ""
echo "=== Next Steps ==="
echo "See DEPLOYMENT_WITHOUT_AZURE.md for complete guide"

View File

@@ -0,0 +1,238 @@
#!/bin/bash
source ~/.bashrc
# Execute All Todo Items - Proxmox Deployment
# Automates execution of all remaining deployment tasks
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
ML110_IP="192.168.1.206"
R630_IP="192.168.1.49"
CLUSTER_NAME="${CLUSTER_NAME:-hc-cluster}"
NFS_SERVER="${NFS_SERVER:-10.10.10.1}"
NFS_PATH="${NFS_PATH:-/mnt/storage}"
STORAGE_NAME="${STORAGE_NAME:-router-storage}"
PVE_ROOT_PASS="${PVE_ROOT_PASS:-}"
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
execute_remote() {
local host=$1
local command=$2
local description=$3
log_info "$description on $host"
if ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 "root@$host" "$command"; then
log_info "$description completed on $host"
return 0
else
log_error "$description failed on $host"
return 1
fi
}
copy_file_remote() {
local host=$1
local source=$2
local dest=$3
log_info "Copying $source to root@$host:$dest"
scp -o StrictHostKeyChecking=no "$source" "root@$host:$dest"
}
# Step 1: Create cluster on ML110
create_cluster_ml110() {
log_step "Creating Proxmox Cluster on ML110"
# Copy cluster setup script
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/proxmox/cluster-setup.sh" "/tmp/cluster-setup.sh"
# Execute cluster creation
execute_remote "$ML110_IP" \
"chmod +x /tmp/cluster-setup.sh && CLUSTER_NAME=$CLUSTER_NAME NODE_ROLE=create /tmp/cluster-setup.sh" \
"Cluster creation"
# Verify
execute_remote "$ML110_IP" "pvecm status && pvecm nodes" "Cluster verification"
}
# Step 2: Join R630 to cluster
join_cluster_r630() {
log_step "Joining R630 to Proxmox Cluster"
# Copy cluster setup script
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/proxmox/cluster-setup.sh" "/tmp/cluster-setup.sh"
# Execute cluster join
if [ -n "$PVE_ROOT_PASS" ]; then
execute_remote "$R630_IP" \
"chmod +x /tmp/cluster-setup.sh && CLUSTER_NAME=$CLUSTER_NAME NODE_ROLE=join CLUSTER_NODE_IP=$ML110_IP ROOT_PASSWORD='$PVE_ROOT_PASS' /tmp/cluster-setup.sh" \
"Cluster join"
else
log_warn "PVE_ROOT_PASS not set, cluster join may require manual password entry"
execute_remote "$R630_IP" \
"chmod +x /tmp/cluster-setup.sh && CLUSTER_NAME=$CLUSTER_NAME NODE_ROLE=join CLUSTER_NODE_IP=$ML110_IP /tmp/cluster-setup.sh" \
"Cluster join"
fi
# Verify
execute_remote "$R630_IP" "pvecm status && pvecm nodes" "Cluster verification"
}
# Step 3: Verify cluster
verify_cluster() {
log_step "Verifying Cluster Health"
log_info "Checking cluster status on ML110..."
execute_remote "$ML110_IP" "pvecm status && pvecm nodes && pvecm expected" "Cluster status check"
log_info "Checking cluster status on R630..."
execute_remote "$R630_IP" "pvecm status && pvecm nodes && pvecm expected" "Cluster status check"
}
# Step 4: Configure NFS storage on ML110
configure_nfs_ml110() {
log_step "Configuring NFS Storage on ML110"
# Copy NFS storage script
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/proxmox/nfs-storage.sh" "/tmp/nfs-storage.sh"
# Execute NFS storage setup
execute_remote "$ML110_IP" \
"chmod +x /tmp/nfs-storage.sh && NFS_SERVER=$NFS_SERVER NFS_PATH=$NFS_PATH STORAGE_NAME=$STORAGE_NAME CONTENT_TYPES=images,iso,vztmpl,backup /tmp/nfs-storage.sh" \
"NFS storage configuration"
# Verify
execute_remote "$ML110_IP" "pvesm status" "Storage verification"
}
# Step 5: Configure NFS storage on R630
configure_nfs_r630() {
log_step "Configuring NFS Storage on R630"
# Copy NFS storage script
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/proxmox/nfs-storage.sh" "/tmp/nfs-storage.sh"
# Execute NFS storage setup
execute_remote "$R630_IP" \
"chmod +x /tmp/nfs-storage.sh && NFS_SERVER=$NFS_SERVER NFS_PATH=$NFS_PATH STORAGE_NAME=$STORAGE_NAME CONTENT_TYPES=images,iso,vztmpl,backup /tmp/nfs-storage.sh" \
"NFS storage configuration"
# Verify
execute_remote "$R630_IP" "pvesm status" "Storage verification"
}
# Step 6: Verify shared storage
verify_storage() {
log_step "Verifying Shared Storage"
log_info "Checking storage on ML110..."
execute_remote "$ML110_IP" "pvesm status && pvesm list" "Storage check"
log_info "Checking storage on R630..."
execute_remote "$R630_IP" "pvesm status && pvesm list" "Storage check"
}
# Step 7: Configure VLAN bridges on ML110
configure_vlans_ml110() {
log_step "Configuring VLAN Bridges on ML110"
# Check if script exists
if [ -f "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" ]; then
copy_file_remote "$ML110_IP" "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" "/tmp/configure-vlans.sh"
execute_remote "$ML110_IP" "chmod +x /tmp/configure-vlans.sh && /tmp/configure-vlans.sh" "VLAN configuration"
else
log_warn "VLAN configuration script not found, skipping"
fi
# Verify
execute_remote "$ML110_IP" "ip addr show | grep -E 'vmbr|vlan'" "Network verification"
}
# Step 8: Configure VLAN bridges on R630
configure_vlans_r630() {
log_step "Configuring VLAN Bridges on R630"
# Check if script exists
if [ -f "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" ]; then
copy_file_remote "$R630_IP" "$PROJECT_ROOT/infrastructure/network/configure-proxmox-vlans.sh" "/tmp/configure-vlans.sh"
execute_remote "$R630_IP" "chmod +x /tmp/configure-vlans.sh && /tmp/configure-vlans.sh" "VLAN configuration"
else
log_warn "VLAN configuration script not found, skipping"
fi
# Verify
execute_remote "$R630_IP" "ip addr show | grep -E 'vmbr|vlan'" "Network verification"
}
# Main execution
main() {
log_info "Starting Proxmox deployment automation..."
log_info "This script will execute all automated tasks"
log_warn "Note: Some tasks (OS installation, manual configuration) require manual intervention"
# Check SSH access
log_info "Testing SSH access..."
if ! ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "root@$ML110_IP" "echo 'ML110 accessible'" &>/dev/null; then
log_error "Cannot SSH to ML110 ($ML110_IP). Please ensure SSH access is configured."
exit 1
fi
if ! ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "root@$R630_IP" "echo 'R630 accessible'" &>/dev/null; then
log_error "Cannot SSH to R630 ($R630_IP). Please ensure SSH access is configured."
exit 1
fi
log_info "SSH access confirmed"
# Execute tasks
create_cluster_ml110
join_cluster_r630
verify_cluster
configure_nfs_ml110
configure_nfs_r630
verify_storage
configure_vlans_ml110
configure_vlans_r630
log_info "Automated tasks completed!"
log_warn "Remaining manual tasks:"
log_warn " - VM template verification/creation"
log_warn " - VM deployment"
log_warn " - OS installation on VMs (requires console access)"
log_warn " - Service configuration"
}
main "$@"

View File

@@ -0,0 +1,160 @@
#!/bin/bash
source ~/.bashrc
# Fix VM Disk Sizes
# Expands disk sizes for VMs cloned from template
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
resize_disk() {
local vmid=$1
local size=$2
local name=$3
log_info "Resizing disk for VM $vmid ($name) to $size..."
local tokens=$(get_api_token)
if [ -z "$tokens" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Get current disk configuration
local current_config=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config")
local current_disk=$(echo "$current_config" | python3 -c "import sys, json; d=json.load(sys.stdin).get('data', {}); print(d.get('scsi0', ''))" 2>/dev/null)
if [ -z "$current_disk" ]; then
log_error "Could not get current disk configuration"
return 1
fi
# Extract storage and disk name
local storage=$(echo "$current_disk" | grep -o 'local-lvm:[^,]*' | cut -d':' -f2 | cut -d'-' -f1-2)
local disk_name=$(echo "$current_disk" | grep -o 'vm-[0-9]*-disk-[0-9]*')
# Stop VM if running
local status=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/current" | \
python3 -c "import sys, json; print(json.load(sys.stdin).get('data', {}).get('status', 'unknown'))" 2>/dev/null)
if [ "$status" = "running" ]; then
log_info "Stopping VM $vmid..."
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/stop" > /dev/null
sleep 5
fi
# Resize disk using resize endpoint
log_info "Resizing disk to $size..."
local resize_response=$(curl -s -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "disk=scsi0" \
-d "size=$size" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/resize" 2>&1)
if echo "$resize_response" | grep -q '"data"'; then
log_info "Disk resized successfully"
else
log_warn "Disk resize response: $resize_response"
# Try alternative method - update config directly
log_info "Trying alternative method..."
curl -s -k -X PUT \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "scsi0=local-lvm:$disk_name,iothread=1,size=$size" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
fi
# Start VM if it was running
if [ "$status" = "running" ]; then
log_info "Starting VM $vmid..."
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start" > /dev/null
fi
return 0
}
main() {
log_info "Fixing VM disk sizes"
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
# VM definitions: vmid name size
local vms=(
"100 cloudflare-tunnel 40G"
"101 k3s-master 80G"
"102 git-server 100G"
"103 observability 200G"
)
for vm_spec in "${vms[@]}"; do
read -r vmid name size <<< "$vm_spec"
resize_disk "$vmid" "$size" "$name"
sleep 2
done
log_info "Disk size fixes completed!"
}
main "$@"

View File

@@ -0,0 +1,337 @@
#!/bin/bash
source ~/.bashrc
# Recreate VMs with Smaller Disk Sizes
# Stops, deletes, and recreates VMs with optimized disk sizes
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "\n${BLUE}=== $1 ===${NC}"
}
# Proxmox configuration
PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
PVE_USERNAME="${PVE_USERNAME:-root@pam}"
PVE_PASSWORD="${PVE_ROOT_PASS:-}"
PROXMOX_NODE="${PROXMOX_NODE:-pve}"
TEMPLATE_VMID="${TEMPLATE_VMID:-9000}"
get_api_token() {
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
stop_and_delete_vm() {
local vmid=$1
local name=$2
log_info "Stopping VM $vmid ($name)..."
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
# Check if VM exists
local vm_status=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/current" 2>&1)
if ! echo "$vm_status" | grep -q '"data"'; then
log_warn "VM $vmid does not exist, skipping"
return 0
fi
# Stop VM if running
local status=$(echo "$vm_status" | python3 -c "import sys, json; print(json.load(sys.stdin).get('data', {}).get('status', 'unknown'))" 2>/dev/null)
if [ "$status" = "running" ]; then
log_info "Stopping VM $vmid..."
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/stop" > /dev/null
# Wait for VM to stop
local wait_count=0
while [ $wait_count -lt 30 ]; do
sleep 2
local current_status=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/current" | \
python3 -c "import sys, json; print(json.load(sys.stdin).get('data', {}).get('status', 'unknown'))" 2>/dev/null)
if [ "$current_status" = "stopped" ]; then
break
fi
wait_count=$((wait_count + 1))
done
if [ $wait_count -ge 30 ]; then
log_error "VM $vmid did not stop in time"
return 1
fi
fi
# Delete VM
log_info "Deleting VM $vmid..."
local delete_response=$(curl -s -k -X DELETE \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid" 2>&1)
if echo "$delete_response" | grep -q '"data"'; then
log_info "VM $vmid deleted successfully"
return 0
else
log_error "Failed to delete VM $vmid: $delete_response"
return 1
fi
}
create_vm_with_smaller_disk() {
local vmid=$1
local name=$2
local ip=$3
local gateway=$4
local cores=$5
local memory=$6
local disk=$7
local bridge=$8
log_info "Creating VM $vmid ($name) with ${disk} disk..."
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
if [ -z "$ticket" ] || [ -z "$csrf_token" ]; then
log_error "Failed to get API tokens"
return 1
fi
# Clone VM from template
log_info "Cloning from template $TEMPLATE_VMID..."
local clone_response=$(curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "newid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_VMID/clone" 2>&1)
if ! echo "$clone_response" | grep -q '"data"'; then
log_error "Failed to clone VM: $clone_response"
return 1
fi
log_info "VM cloned successfully, waiting for clone to complete..."
sleep 5
# Wait for clone to finish
local wait_count=0
while [ $wait_count -lt 30 ]; do
local vm_check=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
if echo "$vm_check" | grep -q '"data"'; then
break
fi
sleep 2
wait_count=$((wait_count + 1))
done
# Configure VM with smaller disk
log_info "Configuring VM $vmid (CPU: $cores, RAM: ${memory}MB, Disk: $disk)..."
# Stop VM if it started automatically
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/stop" > /dev/null 2>&1
sleep 3
# Get current disk configuration
local current_config=$(curl -s -k -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config")
local current_disk=$(echo "$current_config" | python3 -c "import sys, json; d=json.load(sys.stdin).get('data', {}); print(d.get('scsi0', ''))" 2>/dev/null)
# Extract storage pool from current disk or use default
local storage_pool="local-lvm"
if echo "$current_disk" | grep -q ':'; then
storage_pool=$(echo "$current_disk" | cut -d':' -f1)
fi
# Delete old disk and create new smaller one
log_info "Removing old disk and creating new ${disk} disk..."
# Remove old disk
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "scsi0=" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
sleep 2
# Create new disk with smaller size using the disk creation endpoint
log_info "Creating new ${disk} disk..."
local disk_create=$(curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "size=$disk" \
-d "format=raw" \
-d "storage=$storage_pool" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" 2>&1)
# If that doesn't work, try setting it directly in config
if ! echo "$disk_create" | grep -q '"data"'; then
log_info "Trying alternative method: setting disk in config..."
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "scsi0=$storage_pool:vm-$vmid-disk-0,iothread=1,size=$disk" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null 2>&1
fi
# Configure CPU and memory
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "cores=$cores" \
-d "memory=$memory" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Configure network
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "net0=virtio,bridge=$bridge,firewall=1" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Configure QEMU Guest Agent
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "agent=1" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Configure cloud-init
log_info "Configuring cloud-init (user: ubuntu, IP: $ip/24)..."
curl -s -k -X PUT -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
--data-urlencode "ipconfig0=ip=$ip/24,gw=$gateway" \
-d "ciuser=ubuntu" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
log_info "VM $vmid configured successfully with ${disk} disk"
return 0
}
main() {
log_step "Recreating VMs with Smaller Disk Sizes"
if [ -z "$PVE_PASSWORD" ]; then
log_error "PVE_ROOT_PASS not set in .env"
exit 1
fi
log_warn "This will DELETE and RECREATE all VMs with smaller disks!"
log_warn "All data on these VMs will be lost!"
# Check for --yes flag to skip confirmation
if [ "$1" != "--yes" ] && [ "$1" != "-y" ]; then
echo ""
read -p "Are you sure you want to continue? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
log_info "Cancelled by user"
exit 0
fi
else
log_info "Auto-confirmed (--yes flag provided)"
fi
# VM definitions with smaller disk sizes
# Format: vmid name ip gateway cores memory_mb disk_size bridge
local vms=(
"100 cloudflare-tunnel 192.168.1.60 192.168.1.254 2 4096 20G vmbr0"
"101 k3s-master 192.168.1.188 192.168.1.254 4 8192 40G vmbr0"
"102 git-server 192.168.1.121 192.168.1.254 4 8192 50G vmbr0"
"103 observability 192.168.1.82 192.168.1.254 4 8192 100G vmbr0"
)
# Step 1: Stop and delete existing VMs
log_step "Step 1: Stopping and Deleting Existing VMs"
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway cores memory disk bridge <<< "$vm_spec"
stop_and_delete_vm "$vmid" "$name"
sleep 2
done
# Step 2: Recreate VMs with smaller disks
log_step "Step 2: Creating VMs with Smaller Disks"
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway cores memory disk bridge <<< "$vm_spec"
create_vm_with_smaller_disk "$vmid" "$name" "$ip" "$gateway" "$cores" "$memory" "$disk" "$bridge"
sleep 3
done
# Step 3: Start all VMs
log_step "Step 3: Starting All VMs"
local tokens=$(get_api_token)
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
for vm_spec in "${vms[@]}"; do
read -r vmid name ip gateway cores memory disk bridge <<< "$vm_spec"
log_info "Starting VM $vmid ($name)..."
curl -s -k -X POST -H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start" > /dev/null
sleep 2
done
log_step "Recreation Complete!"
log_info "All VMs recreated with smaller disk sizes:"
log_info " VM 100: 20G (was 40G)"
log_info " VM 101: 40G (was 80G)"
log_info " VM 102: 50G (was 100G)"
log_info " VM 103: 100G (was 200G)"
log_info "Total saved: 210GB"
}
main "$@"

View File

@@ -0,0 +1,262 @@
#!/bin/bash
source ~/.bashrc
# Run and Complete All Next Steps
# Comprehensive script to complete all remaining deployment tasks
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load environment variables
if [ -f "$PROJECT_ROOT/.env" ]; then
set -a
source <(grep -v '^#' "$PROJECT_ROOT/.env" | grep -v '^$' | sed 's/#.*$//' | grep '=')
set +a
fi
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "\n${BLUE}=== $1 ===${NC}"; }
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
SSH_OPTS="-i $SSH_KEY -o StrictHostKeyChecking=no"
VM_USER="${VM_USER:-ubuntu}"
# VM definitions: vmid name cores memory disk_size
VMS=(
"100 cloudflare-tunnel 2 2048 20"
"101 k3s-master 4 4096 40"
"102 git-server 2 2048 30"
)
TEMPLATE_VMID=9000
# Helper functions will be sourced on Proxmox host via SSH
# We don't source locally since qm command is not available
# Step 1: Create missing VMs from improved template
create_missing_vms() {
log_step "Step 1: Creating Missing VMs from Template 9000"
local tokens=$(get_api_token)
if [ -z "$tokens" ]; then
log_error "Failed to authenticate with Proxmox"
return 1
fi
local ticket=$(echo "$tokens" | cut -d'|' -f1)
local csrf_token=$(echo "$tokens" | cut -d'|' -f2)
local PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
local PROXMOX_NODE="${PROXMOX_NODE:-pve}"
# Read SSH key
local ssh_key_file="$SSH_KEY.pub"
if [ ! -f "$ssh_key_file" ]; then
log_error "SSH key file not found: $ssh_key_file"
return 1
fi
local ssh_key_content=$(cat "$ssh_key_file")
for vm_spec in "${VMS[@]}"; do
read -r vmid name cores memory disk_size <<< "$vm_spec"
# Check if VM already exists
if ssh $SSH_OPTS "root@$PROXMOX_HOST" "qm config $vmid &>/dev/null"; then
log_info "VM $vmid ($name) already exists, skipping"
continue
fi
log_info "Creating VM $vmid: $name (cores=$cores, memory=${memory}MB, disk=${disk_size}G)"
# Clone from template
local clone_response=$(curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "newid=$vmid" \
-d "name=$name" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$TEMPLATE_VMID/clone" 2>&1)
if ! echo "$clone_response" | grep -q '"data"'; then
log_error "Failed to clone VM: $clone_response"
continue
fi
log_info "Waiting for clone to complete..."
sleep 10
# Configure VM resources
log_info "Configuring VM resources..."
curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
-d "cores=$cores" \
-d "memory=$memory" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Resize disk if needed
if [ "$disk_size" != "32" ]; then
log_info "Resizing disk to ${disk_size}G..."
ssh $SSH_OPTS "root@$PROXMOX_HOST" "qm disk resize $vmid scsi0 ${disk_size}G" 2>/dev/null || true
fi
# Configure cloud-init with SSH keys and DHCP
log_info "Configuring cloud-init with SSH keys..."
curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
--data-urlencode "ipconfig0=ip=dhcp" \
--data-urlencode "ciuser=ubuntu" \
--data-urlencode "sshkeys=${ssh_key_content}" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/config" > /dev/null
# Start VM
log_info "Starting VM $vmid..."
curl -s -k -X POST \
-H "Cookie: PVEAuthCookie=$ticket" \
-H "CSRFPreventionToken: $csrf_token" \
"$PROXMOX_URL/api2/json/nodes/$PROXMOX_NODE/qemu/$vmid/status/start" > /dev/null
log_info "✓ VM $vmid created and started"
done
log_info "Waiting 60 seconds for VMs to boot..."
sleep 60
}
get_api_token() {
local PROXMOX_URL="${PROXMOX_ML110_URL:-https://192.168.1.206:8006}"
local PVE_USERNAME="${PVE_USERNAME:-root@pam}"
local PVE_PASSWORD="${PVE_ROOT_PASS:-}"
local response=$(curl -s -k --connect-timeout 10 --max-time 15 \
-d "username=$PVE_USERNAME&password=$PVE_PASSWORD" \
"$PROXMOX_URL/api2/json/access/ticket" 2>&1)
if echo "$response" | grep -q '"data"'; then
local ticket=$(echo "$response" | grep -o '"ticket":"[^"]*' | cut -d'"' -f4)
local csrf_token=$(echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*' | cut -d'"' -f4)
echo "$ticket|$csrf_token"
else
echo ""
fi
}
# Step 2: Verify SSH and QGA for all VMs
verify_vms() {
log_step "Step 2: Verifying VMs (SSH and QGA)"
local all_vms=("100 cloudflare-tunnel" "101 k3s-master" "102 git-server" "103 observability")
local all_ok=true
for vm_spec in "${all_vms[@]}"; do
read -r vmid name <<< "$vm_spec"
log_info "Checking VM $vmid ($name)..."
# Get IP via guest agent (running on Proxmox host)
local ip
ip=$(ssh $SSH_OPTS "root@$PROXMOX_HOST" \
"source /home/intlc/projects/loc_az_hci/scripts/lib/proxmox_vm_helpers.sh 2>/dev/null && \
get_vm_ip_from_guest_agent $vmid 2>/dev/null || echo ''" 2>/dev/null || echo "")
if [[ -z "$ip" ]]; then
log_warn " VM $vmid: Could not get IP (may still be booting)"
all_ok=false
continue
fi
log_info " IP: $ip"
# Test SSH
if ssh $SSH_OPTS -o ConnectTimeout=5 "${VM_USER}@${ip}" "echo 'SSH OK'" &>/dev/null; then
log_info " ✓ SSH working"
# Check QGA
if ssh $SSH_OPTS "${VM_USER}@${ip}" "systemctl is-active qemu-guest-agent &>/dev/null && echo 'active' || echo 'inactive'" | grep -q "active"; then
log_info " ✓ QEMU Guest Agent active"
else
log_warn " ⚠ QEMU Guest Agent not active (should be pre-installed from template)"
fi
else
log_warn " ✗ SSH not working yet"
all_ok=false
fi
done
if [ "$all_ok" = false ]; then
log_warn "Some VMs may need more time to boot. Continuing anyway..."
fi
}
# Step 3: Deploy Gitea on VM 102
deploy_gitea() {
log_step "Step 3: Deploying Gitea on VM 102"
if [ -f "$PROJECT_ROOT/scripts/deploy/deploy-gitea.sh" ]; then
"$PROJECT_ROOT/scripts/deploy/deploy-gitea.sh"
else
log_warn "Gitea deployment script not found, skipping"
fi
}
# Step 4: Deploy Observability on VM 103
deploy_observability() {
log_step "Step 4: Deploying Observability Stack on VM 103"
if [ -f "$PROJECT_ROOT/scripts/deploy/deploy-observability.sh" ]; then
"$PROJECT_ROOT/scripts/deploy/deploy-observability.sh"
else
log_warn "Observability deployment script not found, skipping"
fi
}
# Step 5: Final Status Report
final_status() {
log_step "Final Status Report"
log_info "VM Status:"
ssh $SSH_OPTS "root@$PROXMOX_HOST" "qm list | grep -E '(100|101|102|103)'"
echo ""
log_info "VM IPs (via Guest Agent):"
local all_vms=("100 cloudflare-tunnel" "101 k3s-master" "102 git-server" "103 observability")
for vm_spec in "${all_vms[@]}"; do
read -r vmid name <<< "$vm_spec"
local ip
ip=$(ssh $SSH_OPTS "root@$PROXMOX_HOST" \
"source /home/intlc/projects/loc_az_hci/scripts/lib/proxmox_vm_helpers.sh 2>/dev/null && \
get_vm_ip_from_guest_agent $vmid 2>/dev/null || echo 'N/A'")
log_info " VM $vmid ($name): $ip"
done
echo ""
log_info "Service URLs:"
log_info " Gitea: http://<VM-102-IP>:3000"
log_info " Prometheus: http://<VM-103-IP>:9090"
log_info " Grafana: http://<VM-103-IP>:3000 (admin/admin)"
echo ""
log_info "✓ All next steps completed!"
}
main() {
log_step "Running All Next Steps"
create_missing_vms
verify_vms
deploy_gitea
deploy_observability
final_status
}
main "$@"

View File

@@ -0,0 +1,127 @@
#!/bin/bash
source ~/.bashrc
# Verify Cloud-init Installation on VMs
# Checks if cloud-init is installed and working
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# SSH user
SSH_USER="${SSH_USER:-ubuntu}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/id_ed25519_proxmox}"
PROXMOX_HOST="${PROXMOX_ML110_IP:-192.168.1.206}"
# Import helper library
if [ -f "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh" ]; then
source "$PROJECT_ROOT/scripts/lib/proxmox_vm_helpers.sh"
else
echo "[ERROR] Helper library not found. Run this script on Proxmox host or via SSH." >&2
exit 1
fi
# VMID NAME (no IP - discovered via guest agent)
VMS=(
"100 cloudflare-tunnel"
"101 k3s-master"
"102 git-server"
"103 observability"
)
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
check_cloud_init() {
local vmid=$1
local name=$2
log_info "Checking cloud-init on $name (VM $vmid)..."
# Ensure guest agent is enabled
ensure_guest_agent_enabled "$vmid" || true
# Get IP from guest agent
local ip
ip="$(get_vm_ip_or_warn "$vmid" "$name" || true)"
if [[ -z "$ip" ]]; then
log_warn "$name (VM $vmid) - cannot get IP from guest agent"
return 1
fi
log_info " Discovered IP: $ip"
# Test connectivity
if ! ping -c 1 -W 2 "$ip" &> /dev/null; then
log_warn "$name ($ip) is not reachable - may still be booting"
return 1
fi
# Try SSH connection
if ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 "$SSH_USER@$ip" "echo 'Connected'" &>/dev/null; then
log_info " SSH connection successful"
# Check cloud-init
local cloud_init_status=$(ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new "$SSH_USER@$ip" \
"systemctl is-active cloud-init 2>/dev/null || echo 'not-installed'" 2>/dev/null)
if [ "$cloud_init_status" = "active" ] || [ "$cloud_init_status" = "inactive" ]; then
log_info " ✓ Cloud-init is installed"
ssh -i "$SSH_KEY" -o StrictHostKeyChecking=accept-new "$SSH_USER@$ip" \
"cloud-init status 2>/dev/null || echo 'Status unknown'" 2>/dev/null
return 0
else
log_warn " Cloud-init may not be installed"
return 1
fi
else
log_warn " Cannot SSH to $name ($ip) - may need password or key"
log_info " To verify manually: ssh -i $SSH_KEY $SSH_USER@$ip"
return 1
fi
}
main() {
log_info "Verifying cloud-init installation on VMs"
log_warn "This requires SSH access to VMs"
log_info "Using guest-agent IP discovery"
echo ""
local all_ok=true
for vm_spec in "${VMS[@]}"; do
read -r vmid name <<< "$vm_spec"
if ! check_cloud_init "$vmid" "$name"; then
all_ok=false
fi
echo ""
done
if [ "$all_ok" = true ]; then
log_info "All VMs have cloud-init installed!"
else
log_warn "Some VMs may not have cloud-init or are not accessible"
log_info "If cloud-init is not installed, install it:"
log_info " sudo apt update && sudo apt install cloud-init"
fi
}
main "$@"