# CoreDNS Service Manager # Start, stop, and manage CoreDNS service # ../utils/log.nu does not exist — dangling import removed (ADR-025 L2). use domain/coredns/corefile.nu [generate-corefile write-corefile] use zones.nu create-zone-file # Start CoreDNS service export def start-coredns [ config: record # CoreDNS configuration --foreground (-f) # Run in foreground --check # Check mode (don't actually start) ] -> bool { log info "Starting CoreDNS service" if $check { log info "Check mode: Would start CoreDNS" return true } let mode = $config.mode? | default "local" if $mode == "disabled" { log warn "CoreDNS is disabled in configuration" return false } if $mode != "local" { log warn $"Cannot start CoreDNS in ($mode) mode" return false } let local_config = $config.local? | default {} let deployment_type = $local_config.deployment_type? | default "binary" match $deployment_type { "binary" => { start-coredns-binary $config $foreground } "docker" => { start-coredns-docker $config } _ => { log error $"Unknown deployment type: ($deployment_type)" false } } } # Start CoreDNS as binary def start-coredns-binary [ config: record foreground: bool ] -> bool { let local_config = $config.local? | default {} let binary_path = $local_config.binary_path? | default "~/.provisioning/bin/coredns" | path expand let config_path = $local_config.config_path? | default "~/.provisioning/coredns/Corefile" | path expand let zones_path = $local_config.zones_path? | default "~/.provisioning/coredns/zones" | path expand let pid_file = $local_config.pid_file? | default "~/.provisioning/coredns/coredns.pid" | path expand let log_file = $local_config.log_file? | default "~/.provisioning/coredns/coredns.log" | path expand # Check if CoreDNS binary exists if not ($binary_path | path exists) { log error $"CoreDNS binary not found at ($binary_path)" log info "Install CoreDNS with: provisioning dns install" return false } # Check if already running if is-coredns-running { log warn "CoreDNS is already running" return false } # Ensure directories exist let config_dir = $config_path | path dirname let zones_dir = $zones_path let log_dir = $log_file | path dirname mkdir $config_dir mkdir $zones_dir mkdir $log_dir # Generate Corefile log info "Generating Corefile" let corefile_content = generate-corefile $config write-corefile $config_path $corefile_content # Create zone files if they don't exist let zones = $local_config.zones? | default ["provisioning.local"] for zone in $zones { let zone_file = $"($zones_path)/($zone).zone" if not ($zone_file | path exists) { log info $"Creating zone file for ($zone)" create-zone-file $zone $zones_path --config $config } } # Start CoreDNS if $foreground { log info "Starting CoreDNS in foreground" let result = (do { ^$binary_path -conf $config_path } | complete) if $result.exit_code == 0 { true } else { log error "Failed to start CoreDNS" false } } else { log info "Starting CoreDNS in background" let result = (do { # Start in background and capture PID nu -c $"^($binary_path) -conf ($config_path) > ($log_file) 2>&1 & echo $\"($env.LAST_EXIT_CODE)\"" } | complete) if $result.exit_code != 0 { log error "Failed to start CoreDNS" return false } # Give it a moment to start sleep 1sec # Check if process is running if is-coredns-running { log info "CoreDNS started successfully" true } else { log error "CoreDNS failed to start, check logs" false } } } # Start CoreDNS in Docker def start-coredns-docker [ config: record ] -> bool { log info "Starting CoreDNS in Docker" let local_config = $config.local? | default {} let docker_config = $local_config.docker? | default {} let image = $docker_config.image? | default "coredns/coredns:1.11.1" let container_name = $docker_config.container_name? | default "provisioning-coredns" let config_path = $local_config.config_path? | default "~/.provisioning/coredns/Corefile" | path expand let zones_path = $local_config.zones_path? | default "~/.provisioning/coredns/zones" | path expand let port = $local_config.port? | default 5353 # Check if container already running let running = (docker ps --filter $"name=($container_name)" --format "{{.Names}}" | complete | get stdout | str trim) if ($running | str contains $container_name) { log warn "CoreDNS container is already running" return false } # Generate Corefile let corefile_content = generate-corefile $config write-corefile $config_path $corefile_content # Create zone files let zones = $local_config.zones? | default ["provisioning.local"] for zone in $zones { let zone_file = $"($zones_path)/($zone).zone" if not ($zone_file | path exists) { create-zone-file $zone $zones_path --config $config } } # Start Docker container let result = (do { docker run -d \ --name $container_name \ -p $"($port):53/udp" \ -p $"($port):53/tcp" \ -v $"($config_path):/Corefile:ro" \ -v $"($zones_path):/zones:ro" \ --restart unless-stopped \ $image -conf /Corefile } | complete) if $result.exit_code == 0 { log info $"CoreDNS Docker container started: [$container_name]" true } else { log error "Failed to start CoreDNS Docker container" false } } # Stop CoreDNS service export def stop-coredns [ --config: record = {} --check ] -> bool { log info "Stopping CoreDNS service" if $check { log info "Check mode: Would stop CoreDNS" return true } let local_config = $config.local? | default {} let deployment_type = $local_config.deployment_type? | default "binary" match $deployment_type { "binary" => { stop-coredns-binary } "docker" => { stop-coredns-docker $config } _ => { log error $"Unknown deployment type: ($deployment_type)" false } } } # Stop CoreDNS binary def stop-coredns-binary [] -> bool { if not (is-coredns-running) { log warn "CoreDNS is not running" return false } let pid = get-coredns-pid if $pid == null { log error "Cannot determine CoreDNS PID" return false } let result = (do { kill $pid } | complete) if $result.exit_code == 0 { log info "CoreDNS stopped" true } else { log error "Failed to stop CoreDNS" false } } # Stop CoreDNS Docker container def stop-coredns-docker [ config: record ] -> bool { let local_config = $config.local? | default {} let docker_config = $local_config.docker? | default {} let container_name = $docker_config.container_name? | default "provisioning-coredns" let result = (do { docker stop $container_name docker rm $container_name } | complete) if $result.exit_code == 0 { log info $"CoreDNS Docker container stopped: [$container_name]" true } else { log error "Failed to stop CoreDNS Docker container" false } } # Reload CoreDNS configuration export def reload-coredns [ --config: record = {} ] -> bool { log info "Reloading CoreDNS configuration" let local_config = $config.local? | default {} let deployment_type = $local_config.deployment_type? | default "binary" match $deployment_type { "binary" => { reload-coredns-binary } "docker" => { reload-coredns-docker $config } _ => { log error $"Unknown deployment type: ($deployment_type)" false } } } # Reload CoreDNS binary (SIGUSR1) def reload-coredns-binary [] -> bool { if not (is-coredns-running) { log error "CoreDNS is not running" return false } let pid = get-coredns-pid if $pid == null { log error "Cannot determine CoreDNS PID" return false } let result = (do { kill -s USR1 $pid } | complete) if $result.exit_code == 0 { log info "CoreDNS reload signal sent" true } else { log error "Failed to reload CoreDNS" false } } # Reload CoreDNS Docker container def reload-coredns-docker [ config: record ] -> bool { let local_config = $config.local? | default {} let docker_config = $local_config.docker? | default {} let container_name = $docker_config.container_name? | default "provisioning-coredns" let result = (do { # Send SIGUSR1 to CoreDNS process inside container docker exec $container_name kill -USR1 1 } | complete) if $result.exit_code == 0 { log info "CoreDNS Docker container reloaded" true } else { log error "Failed to reload CoreDNS Docker container" false } } # Get CoreDNS status export def get-coredns-status [ --config: record = {} ] -> record { let local_config = $config.local? | default {} let deployment_type = $local_config.deployment_type? | default "binary" let running = is-coredns-running let pid = if $running { get-coredns-pid } else { null } let health = if $running { check-coredns-health $config } else { false } { running: $running deployment_type: $deployment_type pid: $pid healthy: $health mode: ($config.mode? | default "local") } } # Check if CoreDNS is running def is-coredns-running [] -> bool { let pid_file = "~/.provisioning/coredns/coredns.pid" | path expand # Check via PID file if ($pid_file | path exists) { let pid = open $pid_file | into int let ps_result = ps | where pid == $pid if ($ps_result | is-not-empty) { return true } } # Check via process name let ps_result = ps | where name =~ "coredns" ($ps_result | is-not-empty) } # Get CoreDNS PID def get-coredns-pid [] -> int { let pid_file = "~/.provisioning/coredns/coredns.pid" | path expand if ($pid_file | path exists) { open $pid_file | into int } else { let ps_result = ps | where name =~ "coredns" if ($ps_result | is-not-empty) { $ps_result | get 0.pid } else { null } } } # Check CoreDNS health export def check-coredns-health [ config: record ] -> bool { log debug "Checking CoreDNS health" let local_config = $config.local? | default {} let port = $local_config.port? | default 5353 # Try to query DNS let result = (do { dig @127.0.0.1 -p $port provisioning.local } | complete) if $result.exit_code == 0 { log debug "CoreDNS health check passed" true } else { log warn "CoreDNS health check failed" false } } # Install CoreDNS binary export def install-coredns [ version?: string = "latest" # Version to install --check ] -> bool { log info $"Installing CoreDNS ($version)" if $check { log info "Check mode: Would install CoreDNS" return true } let binary_path = "~/.provisioning/bin/coredns" | path expand let bin_dir = $binary_path | path dirname # Create bin directory mkdir $bin_dir # Determine latest version if needed let install_version = if $version == "latest" { get-latest-coredns-version } else { $version } log info $"Installing CoreDNS version ($install_version)" # Detect OS and architecture let os = if (sys host | get name) =~ "Darwin" { "darwin" } else { "linux" } let arch = uname -m | complete | get stdout | str trim let arch_name = if $arch == "x86_64" { "amd64" } else if $arch == "aarch64" or $arch == "arm64" { "arm64" } else { $arch } # Download URL let download_url = $"https://github.com/coredns/coredns/releases/download/v($install_version)/coredns_($install_version)_($os)_($arch_name).tgz" log info $"Downloading from ($download_url)" let temp_dir = mktemp -d let result = (do { cd $temp_dir http get $download_url | save coredns.tgz tar xzf coredns.tgz mv coredns $binary_path chmod +x $binary_path } | complete) if $result.exit_code == 0 { log info $"CoreDNS installed to [$binary_path]" # Cleanup rm -rf $temp_dir true } else { log error "Failed to install CoreDNS" rm -rf $temp_dir false } } # Get latest CoreDNS version from GitHub def get-latest-coredns-version [] -> string { let result = (do { http get "https://api.github.com/repos/coredns/coredns/releases/latest" | from json } | complete) if $result.exit_code == 0 { let release_info = $result.stdout | from json $release_info.tag_name | str replace "v" "" } else { log warn "Failed to get latest version, using default" "1.11.1" } } # Restart CoreDNS service export def restart-coredns [ config: record --check ] -> bool { log info "Restarting CoreDNS service" if $check { log info "Check mode: Would restart CoreDNS" return true } stop-coredns --config $config sleep 1sec start-coredns $config } # Show CoreDNS logs export def show-coredns-logs [ --lines: int = 50 --follow (-f) ] -> nothing { let log_file = "~/.provisioning/coredns/coredns.log" | path expand if not ($log_file | path exists) { log error "CoreDNS log file not found" return } if $follow { tail -f -n $lines $log_file } else { tail -n $lines $log_file } }