diff --git a/k8s/deployment/templates/dns-endpoint.yaml.tpl b/k8s/deployment/templates/dns-endpoint.yaml.tpl new file mode 100644 index 00000000..e68e1903 --- /dev/null +++ b/k8s/deployment/templates/dns-endpoint.yaml.tpl @@ -0,0 +1,22 @@ +apiVersion: externaldns.k8s.io/v1alpha1 +kind: DNSEndpoint +metadata: + name: k-8-s-{{ .scope.slug }}-{{ .scope.id }}-dns + namespace: {{ .k8s_namespace }} + labels: + nullplatform: "true" + account: {{ .account.slug }} + account_id: "{{ .account.id }}" + namespace: {{ .namespace.slug }} + namespace_id: "{{ .namespace.id }}" + application: {{ .application.slug }} + application_id: "{{ .application.id }}" + scope: {{ .scope.slug }} + scope_id: "{{ .scope.id }}" +spec: + endpoints: + - dnsName: {{ .scope.domain }} + recordTTL: 60 + recordType: A + targets: + - "{{ .gateway_ip }}" diff --git a/k8s/deployment/workflows/initial.yaml b/k8s/deployment/workflows/initial.yaml index fc225f4d..63ac9d15 100644 --- a/k8s/deployment/workflows/initial.yaml +++ b/k8s/deployment/workflows/initial.yaml @@ -61,4 +61,5 @@ steps: type: script file: "$SERVICE_PATH/deployment/wait_deployment_active" configuration: - TIMEOUT: DEPLOYMENT_MAX_WAIT_IN_SECONDS \ No newline at end of file + TIMEOUT: DEPLOYMENT_MAX_WAIT_IN_SECONDS + diff --git a/k8s/scope/build_context b/k8s/scope/build_context index 2b769169..1e545a58 100755 --- a/k8s/scope/build_context +++ b/k8s/scope/build_context @@ -31,10 +31,12 @@ export SCOPE_DOMAIN if [ "$SCOPE_VISIBILITY" = "public" ]; then export INGRESS_VISIBILITY="internet-facing" - export GATEWAY_NAME="${PUBLIC_GATEWAY_NAME:-gateway-public}" + GATEWAY_DEFAULT="${PUBLIC_GATEWAY_NAME:-gateway-public}" + export GATEWAY_NAME=$(echo "$CONTEXT" | jq -r --arg default "$GATEWAY_DEFAULT" '.providers["container-orchestration"].gateway.public_name // $default') else export INGRESS_VISIBILITY="internal" - export GATEWAY_NAME="${PRIVATE_GATEWAY_NAME:-gateway-internal}" + GATEWAY_DEFAULT="${PRIVATE_GATEWAY_NAME:-gateway-internal}" + export GATEWAY_NAME=$(echo "$CONTEXT" | jq -r --arg default "$GATEWAY_DEFAULT" '.providers["container-orchestration"].gateway.private_name // $default') fi K8S_MODIFIERS="${K8S_MODIFIERS:-"{}"}" diff --git a/k8s/scope/networking/dns/build_dns_context b/k8s/scope/networking/dns/build_dns_context index 5b81a71b..9df57eb5 100755 --- a/k8s/scope/networking/dns/build_dns_context +++ b/k8s/scope/networking/dns/build_dns_context @@ -18,6 +18,10 @@ case "$DNS_TYPE" in echo "PUBLIC_GATEWAY_NAME: $PUBLIC_GATEWAY_NAME" echo "PRIVATE_GATEWAY_NAME: $PRIVATE_GATEWAY_NAME" ;; + external_dns) + echo "external_dns context ready" + echo "DNS records will be managed automatically by External DNS operator" + ;; *) echo "Error: Unsupported DNS type '$DNS_TYPE'" exit 1 diff --git a/k8s/scope/networking/dns/external_dns/manage_route b/k8s/scope/networking/dns/external_dns/manage_route new file mode 100644 index 00000000..7c8cfdf4 --- /dev/null +++ b/k8s/scope/networking/dns/external_dns/manage_route @@ -0,0 +1,59 @@ +#!/bin/bash + +set -euo pipefail + +if [ "$ACTION" = "CREATE" ]; then + echo "Building DNSEndpoint manifest for ExternalDNS..." + + echo "Getting IP for gateway: $GATEWAY_NAME" + + GATEWAY_IP=$(kubectl get gateway "$GATEWAY_NAME" -n gateways \ + -o jsonpath='{.status.addresses[?(@.type=="IPAddress")].value}' 2>/dev/null) + + if [ -z "$GATEWAY_IP" ]; then + echo "Warning: Could not get gateway IP for $GATEWAY_NAME" + + GATEWAY_IP=$(kubectl get service "$GATEWAY_NAME" -n gateways \ + -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null) + fi + + if [ -z "$GATEWAY_IP" ]; then + echo "Warning: Could not determine gateway IP address yet, DNSEndpoint will be created later" + exit 0 + fi + + echo "Gateway IP: $GATEWAY_IP" + + DNS_ENDPOINT_TEMPLATE="${DNS_ENDPOINT_TEMPLATE:-$SERVICE_PATH/deployment/templates/dns-endpoint.yaml.tpl}" + + if [ -f "$DNS_ENDPOINT_TEMPLATE" ]; then + DNS_ENDPOINT_FILE="$OUTPUT_DIR/dns-endpoint-$SCOPE_ID.yaml" + CONTEXT_PATH="$OUTPUT_DIR/context-$SCOPE_ID-dns.json" + + echo "$CONTEXT" | jq --arg gateway_ip "$GATEWAY_IP" '. + {gateway_ip: $gateway_ip}' > "$CONTEXT_PATH" + + echo "Building DNSEndpoint Template: $DNS_ENDPOINT_TEMPLATE to $DNS_ENDPOINT_FILE" + + gomplate -c .="$CONTEXT_PATH" \ + --file "$DNS_ENDPOINT_TEMPLATE" \ + --out "$DNS_ENDPOINT_FILE" + + echo "DNSEndpoint manifest created at: $DNS_ENDPOINT_FILE" + + rm "$CONTEXT_PATH" + + else + echo "Error: DNSEndpoint template not found at $DNS_ENDPOINT_TEMPLATE" + exit 1 + fi + +elif [ "$ACTION" = "DELETE" ]; then + echo "Deleting DNSEndpoint for external_dns..." + + SCOPE_SLUG=$(echo "$CONTEXT" | jq -r '.scope.slug') + DNS_ENDPOINT_NAME="k-8-s-${SCOPE_SLUG}-${SCOPE_ID}-dns" + echo "Attempting to delete DNSEndpoint by name: $DNS_ENDPOINT_NAME" + kubectl delete dnsendpoint "$DNS_ENDPOINT_NAME" -n "$K8S_NAMESPACE" || echo "DNSEndpoint may already be deleted" + + echo "DNSEndpoint deletion completed" +fi \ No newline at end of file diff --git a/k8s/scope/networking/dns/manage_dns b/k8s/scope/networking/dns/manage_dns index eed162f9..f5fd1202 100755 --- a/k8s/scope/networking/dns/manage_dns +++ b/k8s/scope/networking/dns/manage_dns @@ -35,6 +35,13 @@ case "$DNS_TYPE" in --hosted-zone-name="$HOSTED_ZONE_NAME" \ --hosted-zone-rg="$HOSTED_ZONE_RG" ;; + external_dns) + echo "Using external_dns provider" + source "$SERVICE_PATH/scope/networking/dns/external_dns/manage_route" || { + echo "ERROR: External DNS management failed" + exit 1 + } + ;; *) echo "Error: Unsupported dns type '$DNS_TYPE'" exit 1 diff --git a/k8s/scope/networking/gateway/build_gateway b/k8s/scope/networking/gateway/build_gateway index b1e4e38a..8d97e2da 100755 --- a/k8s/scope/networking/gateway/build_gateway +++ b/k8s/scope/networking/gateway/build_gateway @@ -15,4 +15,5 @@ gomplate -c .="$CONTEXT_PATH" \ --file "$TEMPLATE" \ --out "$INGRESS_FILE" + rm "$CONTEXT_PATH" \ No newline at end of file diff --git a/k8s/scope/wait_on_balancer b/k8s/scope/wait_on_balancer new file mode 100644 index 00000000..9f9edf88 --- /dev/null +++ b/k8s/scope/wait_on_balancer @@ -0,0 +1,59 @@ +#!/bin/bash + +echo "Waiting for balancer/DNS setup to complete..." + +MAX_ITERATIONS=${MAX_ITERATIONS:-30} +iteration=0 + +case "$DNS_TYPE" in + external_dns) + SCOPE_DOMAIN=$(echo "$CONTEXT" | jq -r '.scope.domain') + SCOPE_SLUG=$(echo "$CONTEXT" | jq -r '.scope.slug') + SCOPE_ID=$(echo "$CONTEXT" | jq -r '.scope.id') + + echo "Checking ExternalDNS record creation for domain: $SCOPE_DOMAIN" + + while true; do + iteration=$((iteration + 1)) + if [ $iteration -gt $MAX_ITERATIONS ]; then + echo "⚠️ DNS record creation timeout after $((MAX_ITERATIONS * 10))s" + echo "ExternalDNS may still be processing the DNSEndpoint resource" + echo "You can check manually with: kubectl get dnsendpoint -A" + exit 1 + fi + + echo "Checking DNS resolution for $SCOPE_DOMAIN (attempt $iteration/$MAX_ITERATIONS)" + + DNS_ENDPOINT_NAME="k-8-s-${SCOPE_SLUG}-${SCOPE_ID}-dns" + echo "Checking DNSEndpoint status: $DNS_ENDPOINT_NAME" + + DNS_STATUS=$(kubectl get dnsendpoint "$DNS_ENDPOINT_NAME" -n "$K8S_NAMESPACE" -o jsonpath='{.status}' 2>/dev/null || echo "not found") + + if [ "$DNS_STATUS" != "not found" ] && [ -n "$DNS_STATUS" ]; then + echo "DNSEndpoint status: $DNS_STATUS" + fi + + if nslookup "$SCOPE_DOMAIN" 8.8.8.8 >/dev/null 2>&1; then + echo "✓ DNS record for $SCOPE_DOMAIN is now resolvable" + + RESOLVED_IP=$(nslookup "$SCOPE_DOMAIN" 8.8.8.8 | grep -A1 "Name:" | tail -1 | awk '{print $2}' 2>/dev/null || echo "unknown") + echo "✓ Domain $SCOPE_DOMAIN resolves to: $RESOLVED_IP" + + break + fi + + echo "DNS record not yet available, waiting 10s..." + sleep 10 + done + + echo "✓ ExternalDNS setup completed successfully" + ;; + route53|azure) + echo "DNS Type $DNS_TYPE - DNS should already be configured" + echo "Skipping DNS wait check" + ;; + *) + echo "Unknown DNS type: $DNS_TYPE" + echo "Skipping DNS wait check" + ;; +esac \ No newline at end of file diff --git a/k8s/scope/workflows/create.yaml b/k8s/scope/workflows/create.yaml index c57caa2a..adb336c5 100644 --- a/k8s/scope/workflows/create.yaml +++ b/k8s/scope/workflows/create.yaml @@ -57,3 +57,16 @@ steps: type: environment - name: HOSTED_PRIVATE_ZONE_ID type: environment + post: + name: apply_and_wait + type: workflow + steps: + - name: apply dns templates + type: script + file: "$SERVICE_PATH/apply_templates" + configuration: + ACTION: apply + DRY_RUN: false + - name: wait on balancer + type: script + file: "$SERVICE_PATH/scope/wait_on_balancer" diff --git a/k8s/values.yaml b/k8s/values.yaml index 8acb9f34..46351563 100644 --- a/k8s/values.yaml +++ b/k8s/values.yaml @@ -5,7 +5,7 @@ configuration: K8S_NAMESPACE: nullplatform DOMAIN: nullapps.io USE_ACCOUNT_SLUG: false - DNS_TYPE: route53 + DNS_TYPE: route53 # Available values route53 | azure | external_dns DEPLOYMENT_MAX_WAIT_IN_SECONDS: 600 DEPLOYMENT_TEMPLATE: "$SERVICE_PATH/deployment/templates/deployment.yaml.tpl" SECRET_TEMPLATE: "$SERVICE_PATH/deployment/templates/secret.yaml.tpl"