provisioning-catalog/providers/hetzner/templates/hetzner_servers.j2

217 lines
8.6 KiB
Text
Raw Permalink Normal View History

{%- set srv = servers | first -%}
SERVER_NAME="{{ srv.hostname }}"
FIREWALL_NAME="{{ srv.firewall | default(value='librecloud-fw') }}"
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
log_error() {
echo -e "${RED}[✗]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
# ============================================================================
# STEP 1: Check if server already exists
# ============================================================================
log_info "Step 1/3: Checking server '$SERVER_NAME'..."
if hcloud server describe "$SERVER_NAME" > /dev/null 2>&1; then
log_warn "Server '$SERVER_NAME' already exists"
SERVER_ID=$(hcloud server describe "$SERVER_NAME" -o yaml 2>/dev/null | grep ^id: | cut -f2 -d":" | sed "s/ //g")
log_success "Using existing server: ID=$SERVER_ID"
else
# ============================================================================
# STEP 1b: Create server
# ============================================================================
log_info "Creating server '$SERVER_NAME'..."
out_err="${STATE_DIR}/${SERVER_NAME}_create_out.text"
set +e
SERVER_INFO=$(hcloud server create \
--name "{{ srv.hostname }}" \
--type {{ srv.server_type }} \
--location {{ srv.location }} \
--image {{ srv.image }} \
{%- if srv.networking is defined and srv.networking.ipv6_enabled is defined and not srv.networking.ipv6_enabled %}
--without-ipv6 \
{%- endif %}
{%- if srv.networking is defined and srv.networking.ipv4_enabled is defined and not srv.networking.ipv4_enabled %}
--without-ipv4 \
{%- endif %}
{%- if srv.networking is defined and srv.networking.ipv4_enabled is defined and not srv.networking.ipv4_enabled and srv.networking.private_network is defined %}
--network "{{ srv.networking.private_network }}" \
{%- endif %}
{%- for key in srv.ssh_keys %}
--ssh-key "{{ key }}" \
{%- endfor %}
{%- for label_key, label_val in srv.labels %}
--label "{{ label_key }}={{ label_val }}" \
{%- endfor %}
2>"$out_err" | tr -d '\n')
HCLOUD_EXIT=$?
set -e
if [ $HCLOUD_EXIT -ne 0 ] || [ -z "$SERVER_INFO" ]; then
log_error "Failed to create server '$SERVER_NAME' (exit=$HCLOUD_EXIT)"
if [ -s "$out_err" ]; then
log_error "hcloud: $(cat "$out_err")"
fi
exit 1
fi
SERVER_ID=$(echo "$SERVER_INFO" | cut -f2 -d" ")
SERVER_STATUS=$(echo "$SERVER_INFO" | cut -f3 -d" ")
SERVER_IPv4=$(echo "$SERVER_INFO" | cut -f5 -d" ")
log_success "$SERVER_ID $SERVER_NAME $SERVER_STATUS $SERVER_IPv4"
log_success "Server created: ID=$SERVER_ID"
# Wait for server to reach running state before network/protection ops
log_info "Waiting for server '$SERVER_NAME' to reach running state..."
for _i in $(seq 1 30); do
_st=$(hcloud server describe "$SERVER_NAME" -o yaml 2>/dev/null | grep ^status: | awk '{print $2}')
[ "$_st" = "running" ] && break
sleep 3
done
log_success "Server '$SERVER_NAME' is running"
fi
if [ -n "$SERVER_ID" ] ; then
{%- if srv.networking is defined and srv.networking.private_network is defined %}
{%- if srv.networking.private_ip is defined and srv.networking.private_ip %}
_net_attached=false
_attach_err="${STATE_DIR}/${SERVER_NAME}_net_attach.err"
# Check if already attached before trying
if HJSON="$(hcloud server describe "$SERVER_NAME" -o json 2>/dev/null)" \
nu -c '$env.HJSON | from json | get -o private_net | default [] | (any {|n| ($n | get -o ip | default "") == "{{ srv.networking.private_ip }}"}) | if $in { exit 0 } else { exit 1 }' 2>/dev/null; then
log_success "Already attached to {{ srv.networking.private_network }} with IP {{ srv.networking.private_ip }}"
_net_attached=true
else
for _r in $(seq 1 6); do
if hcloud server attach-to-network \
--network "{{ srv.networking.private_network }}" --ip "{{ srv.networking.private_ip }}" \
"$SERVER_ID" 2>"$_attach_err"; then
log_success "Attached to network {{ srv.networking.private_network }} with IP {{ srv.networking.private_ip }}"
_net_attached=true
break
fi
_err=$(cat "$_attach_err" 2>/dev/null)
log_info "Attach attempt $_r/6 failed: $_err — retrying in 5s..."
sleep 5
done
[ "$_net_attached" = "false" ] && log_warn "Network attach failed after 6 attempts: $(cat "$_attach_err" 2>/dev/null)"
fi
{%- else %}
_net_attached=false
_attach_err="${STATE_DIR}/${SERVER_NAME}_net_attach.err"
if HJSON="$(hcloud server describe "$SERVER_NAME" -o json 2>/dev/null)" \
nu -c 'if (($env.HJSON | from json | get -o private_net | default []) | is-not-empty) { exit 0 } else { exit 1 }' 2>/dev/null; then
log_success "Already attached to {{ srv.networking.private_network }}"
_net_attached=true
else
for _r in $(seq 1 6); do
if hcloud server attach-to-network \
--network "{{ srv.networking.private_network }}" \
"$SERVER_ID" 2>"$_attach_err"; then
log_success "Attached to network {{ srv.networking.private_network }}"
_net_attached=true
break
fi
_err=$(cat "$_attach_err" 2>/dev/null)
log_info "Attach attempt $_r/6 failed: $_err — retrying in 5s..."
sleep 5
done
[ "$_net_attached" = "false" ] && log_warn "Network attach failed after 6 attempts: $(cat "$_attach_err" 2>/dev/null)"
fi
{%- endif %}
{%- endif %}
{%- if srv.protection is defined %}
# Enable delete/rebuild protection as configured
_prot_args=""
{%- if srv.protection.delete is defined and srv.protection.delete == true %}
_prot_args="$_prot_args delete"
{%- endif %}
{%- if srv.protection.rebuild is defined and srv.protection.rebuild == true %}
_prot_args="$_prot_args rebuild"
{%- endif %}
if [ -n "$_prot_args" ]; then
hcloud server enable-protection "$SERVER_NAME" $_prot_args 2>/dev/null \
&& log_success "Protection enabled:$_prot_args" \
|| log_warn "Failed to enable protection (may already be set)"
fi
{%- endif %}
fi
# ============================================================================
# STEP 2: Attach server to cluster firewall (firewall managed independently)
# ============================================================================
log_info "Step 2/2: Attaching server to firewall '$FIREWALL_NAME'..."
if hcloud firewall describe "$FIREWALL_NAME" > /dev/null 2>&1; then
hcloud firewall apply-to-resource "$FIREWALL_NAME" \
--type server \
--server "$SERVER_NAME" 2>/dev/null || true
log_success "Server attached to firewall '$FIREWALL_NAME'"
else
log_warn "Firewall '$FIREWALL_NAME' not found — run firewall reconciliation first"
fi
{%- if srv.floating_ip is defined and srv.floating_ip %}
# ============================================================================
# STEP 3: Assign floating IP
# ============================================================================
FLOATING_IP_NAME="{{ srv.floating_ip }}"
log_info "Step 3/3: Assigning floating IP '$FLOATING_IP_NAME' to '$SERVER_NAME'..."
set +e
FIP_ID=$(hcloud floating-ip list -o noheader -o columns=id,name 2>/dev/null | awk -v name="$FLOATING_IP_NAME" '$2 == name { print $1 }')
set -e
if [ -z "$FIP_ID" ]; then
log_error "Floating IP '$FLOATING_IP_NAME' not found — run bootstrap first"
exit 1
fi
set +e
hcloud floating-ip assign "$FIP_ID" "$SERVER_ID" 2>"${STATE_DIR}/${SERVER_NAME}_fip_out.text"
FIP_EXIT=$?
set -e
if [ $FIP_EXIT -ne 0 ]; then
log_error "Failed to assign floating IP '$FLOATING_IP_NAME' (id=$FIP_ID) to '$SERVER_NAME'"
if [ -s "${STATE_DIR}/${SERVER_NAME}_fip_out.text" ]; then
log_error "hcloud: $(cat "${STATE_DIR}/${SERVER_NAME}_fip_out.text")"
fi
exit 1
fi
log_success "Assigned floating IP '$FLOATING_IP_NAME' (id=$FIP_ID) → $SERVER_NAME (id=$SERVER_ID)"
{%- endif %}
# ============================================================================
# SUCCESS
# ============================================================================
log_success "✓ Server provisioning complete"
log_info "Summary:"
log_info " • Server: $SERVER_NAME (ID: $SERVER_ID)"
log_info " • Firewall: $FIREWALL_NAME"
{%- if srv.floating_ip is defined and srv.floating_ip %}
log_info " • Float IP: {{ srv.floating_ip }} (id=$FIP_ID)"
{%- endif %}
log_info " • Status: Ready for deployment"