428 lines
11 KiB
Text
428 lines
11 KiB
Text
|
|
# CoreDNS API Client
|
||
|
|
# Client for orchestrator DNS API endpoints
|
||
|
|
|
||
|
|
# ../utils/log.nu does not exist — dangling import removed (ADR-025 L2).
|
||
|
|
use domain/config/loader.nu [get-config]
|
||
|
|
|
||
|
|
# Call orchestrator DNS API
|
||
|
|
export def call-dns-api [
|
||
|
|
endpoint: string # API endpoint path (e.g., "/dns/record")
|
||
|
|
method: string = "GET" # HTTP method
|
||
|
|
body?: record # Request body
|
||
|
|
--timeout: int = 30 # Request timeout in seconds
|
||
|
|
] -> record {
|
||
|
|
log debug $"Calling DNS API: ($method) ($endpoint)"
|
||
|
|
|
||
|
|
let config = get-config
|
||
|
|
let coredns_config = $config.coredns? | default {}
|
||
|
|
let api_endpoint = $coredns_config.dynamic_updates?.api_endpoint? | default "http://localhost:8080/dns"
|
||
|
|
|
||
|
|
let full_url = $"($api_endpoint)($endpoint)"
|
||
|
|
|
||
|
|
let result = (do {
|
||
|
|
match $method {
|
||
|
|
"GET" => {
|
||
|
|
http get -t $timeout $full_url
|
||
|
|
}
|
||
|
|
"POST" => {
|
||
|
|
if $body != null {
|
||
|
|
http post -t $timeout -c "application/json" $full_url ($body | to json)
|
||
|
|
} else {
|
||
|
|
http post -t $timeout $full_url
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"PUT" => {
|
||
|
|
if $body != null {
|
||
|
|
http put -t $timeout -c "application/json" $full_url ($body | to json)
|
||
|
|
} else {
|
||
|
|
http put -t $timeout $full_url
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"DELETE" => {
|
||
|
|
http delete -t $timeout $full_url
|
||
|
|
}
|
||
|
|
_ => {
|
||
|
|
log error $"Unsupported HTTP method: [$method]"
|
||
|
|
error make {msg: "Unsupported method"}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} | complete)
|
||
|
|
|
||
|
|
if $result.exit_code == 0 {
|
||
|
|
{
|
||
|
|
success: true
|
||
|
|
response: $result.stdout
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
log error $"DNS API call failed: [$result.stderr]"
|
||
|
|
{
|
||
|
|
success: false
|
||
|
|
error: $result.stderr
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Add DNS record via API
|
||
|
|
export def api-add-record [
|
||
|
|
hostname: string # Hostname
|
||
|
|
ip_address: string # IP address
|
||
|
|
record_type: string = "A" # Record type
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
--ttl: int = 300
|
||
|
|
--comment: string = ""
|
||
|
|
--check
|
||
|
|
] -> bool {
|
||
|
|
log info $"Adding DNS record via API: ($hostname) -> ($ip_address)"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would add DNS record via API"
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
zone: $zone
|
||
|
|
hostname: $hostname
|
||
|
|
record_type: $record_type
|
||
|
|
value: $ip_address
|
||
|
|
ttl: $ttl
|
||
|
|
comment: $comment
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/record" "POST" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info $"DNS record added via API: ($hostname)"
|
||
|
|
true
|
||
|
|
} else {
|
||
|
|
log error $"Failed to add DNS record via API: ($result.error)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Remove DNS record via API
|
||
|
|
export def api-remove-record [
|
||
|
|
hostname: string # Hostname
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
--check
|
||
|
|
] -> bool {
|
||
|
|
log info $"Removing DNS record via API: ($hostname)"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would remove DNS record via API"
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
zone: $zone
|
||
|
|
hostname: $hostname
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/record" "DELETE" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info $"DNS record removed via API: ($hostname)"
|
||
|
|
true
|
||
|
|
} else {
|
||
|
|
log error $"Failed to remove DNS record via API: ($result.error)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Update DNS record via API
|
||
|
|
export def api-update-record [
|
||
|
|
hostname: string # Hostname
|
||
|
|
ip_address: string # New IP address
|
||
|
|
record_type: string = "A" # Record type
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
--ttl: int = 300
|
||
|
|
--comment: string = ""
|
||
|
|
--check
|
||
|
|
] -> bool {
|
||
|
|
log info $"Updating DNS record via API: ($hostname) -> ($ip_address)"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would update DNS record via API"
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
zone: $zone
|
||
|
|
hostname: $hostname
|
||
|
|
record_type: $record_type
|
||
|
|
value: $ip_address
|
||
|
|
ttl: $ttl
|
||
|
|
comment: $comment
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/record" "PUT" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info $"DNS record updated via API: ($hostname)"
|
||
|
|
true
|
||
|
|
} else {
|
||
|
|
log error $"Failed to update DNS record via API: ($result.error)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# List DNS records via API
|
||
|
|
export def api-list-records [
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
--format: string = "table"
|
||
|
|
] -> any {
|
||
|
|
log debug $"Listing DNS records via API for zone: ($zone)"
|
||
|
|
|
||
|
|
let result = call-dns-api $"/records?zone=($zone)" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
let records = $result.response
|
||
|
|
|
||
|
|
match $format {
|
||
|
|
"json" => { $records | to json }
|
||
|
|
"yaml" => { $records | to yaml }
|
||
|
|
_ => { $records }
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
log error $"Failed to list DNS records via API: ($result.error)"
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get DNS zones via API
|
||
|
|
export def api-list-zones [] -> list {
|
||
|
|
log debug "Listing DNS zones via API"
|
||
|
|
|
||
|
|
let result = call-dns-api "/zones" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to list DNS zones via API: ($result.error)"
|
||
|
|
[]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Reload CoreDNS via API
|
||
|
|
export def api-reload-coredns [] -> bool {
|
||
|
|
log info "Reloading CoreDNS via API"
|
||
|
|
|
||
|
|
let result = call-dns-api "/reload" "POST"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info "CoreDNS reloaded via API"
|
||
|
|
true
|
||
|
|
} else {
|
||
|
|
log error $"Failed to reload CoreDNS via API: ($result.error)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Check DNS health via API
|
||
|
|
export def api-check-health [] -> record {
|
||
|
|
log debug "Checking DNS health via API"
|
||
|
|
|
||
|
|
let result = call-dns-api "/health" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to check DNS health via API: ($result.error)"
|
||
|
|
{
|
||
|
|
healthy: false
|
||
|
|
error: $result.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Batch add DNS records via API
|
||
|
|
export def api-batch-add-records [
|
||
|
|
records: list # List of {hostname, ip_address, record_type, zone, ttl, comment}
|
||
|
|
--check
|
||
|
|
] -> record {
|
||
|
|
log info $"Batch adding ($records | length) DNS records via API"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would batch add DNS records via API"
|
||
|
|
return {
|
||
|
|
total: ($records | length)
|
||
|
|
added: ($records | length)
|
||
|
|
failed: 0
|
||
|
|
check_mode: true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
records: $records
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/records/batch" "POST" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info "DNS records batch added via API"
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to batch add DNS records via API: ($result.error)"
|
||
|
|
{
|
||
|
|
total: ($records | length)
|
||
|
|
added: 0
|
||
|
|
failed: ($records | length)
|
||
|
|
error: $result.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Batch remove DNS records via API
|
||
|
|
export def api-batch-remove-records [
|
||
|
|
hostnames: list<string> # List of hostnames
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
--check
|
||
|
|
] -> record {
|
||
|
|
log info $"Batch removing ($hostnames | length) DNS records via API"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would batch remove DNS records via API"
|
||
|
|
return {
|
||
|
|
total: ($hostnames | length)
|
||
|
|
removed: ($hostnames | length)
|
||
|
|
failed: 0
|
||
|
|
check_mode: true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
zone: $zone
|
||
|
|
hostnames: $hostnames
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/records/batch" "DELETE" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info "DNS records batch removed via API"
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to batch remove DNS records via API: ($result.error)"
|
||
|
|
{
|
||
|
|
total: ($hostnames | length)
|
||
|
|
removed: 0
|
||
|
|
failed: ($hostnames | length)
|
||
|
|
error: $result.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Query DNS via API
|
||
|
|
export def api-query-dns [
|
||
|
|
hostname: string # Hostname to query
|
||
|
|
--type: string = "A" # Record type
|
||
|
|
--zone: string = "provisioning.local"
|
||
|
|
] -> record {
|
||
|
|
log debug $"Querying DNS via API: ($hostname) ($type)"
|
||
|
|
|
||
|
|
let result = call-dns-api $"/query?hostname=($hostname)&type=($type)&zone=($zone)" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to query DNS via API: ($result.error)"
|
||
|
|
{
|
||
|
|
hostname: $hostname
|
||
|
|
type: $type
|
||
|
|
found: false
|
||
|
|
error: $result.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Export zone file via API
|
||
|
|
export def api-export-zone [
|
||
|
|
zone: string # Zone name
|
||
|
|
--output: string = "" # Output file path (optional)
|
||
|
|
] -> string {
|
||
|
|
log debug $"Exporting zone via API: ($zone)"
|
||
|
|
|
||
|
|
let result = call-dns-api $"/zones/($zone)/export" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
let zone_content = $result.response.content
|
||
|
|
|
||
|
|
if ($output | is-not-empty) {
|
||
|
|
$zone_content | save -f $output
|
||
|
|
log info $"Zone exported to ($output)"
|
||
|
|
}
|
||
|
|
|
||
|
|
$zone_content
|
||
|
|
} else {
|
||
|
|
log error $"Failed to export zone via API: ($result.error)"
|
||
|
|
""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Import zone file via API
|
||
|
|
export def api-import-zone [
|
||
|
|
zone: string # Zone name
|
||
|
|
zone_file: string # Path to zone file
|
||
|
|
--check
|
||
|
|
] -> bool {
|
||
|
|
log info $"Importing zone via API: ($zone)"
|
||
|
|
|
||
|
|
if $check {
|
||
|
|
log info "Check mode: Would import zone via API"
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
if not ($zone_file | path exists) {
|
||
|
|
log error $"Zone file not found: ($zone_file)"
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
let zone_content = open $zone_file
|
||
|
|
|
||
|
|
let body = {
|
||
|
|
zone: $zone
|
||
|
|
content: $zone_content
|
||
|
|
}
|
||
|
|
|
||
|
|
let result = call-dns-api "/zones/import" "POST" $body
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
log info $"Zone imported via API: ($zone)"
|
||
|
|
true
|
||
|
|
} else {
|
||
|
|
log error $"Failed to import zone via API: ($result.error)"
|
||
|
|
false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Get DNS statistics via API
|
||
|
|
export def api-get-stats [] -> record {
|
||
|
|
log debug "Getting DNS statistics via API"
|
||
|
|
|
||
|
|
let result = call-dns-api "/stats" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to get DNS statistics via API: ($result.error)"
|
||
|
|
{}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# Validate zone via API
|
||
|
|
export def api-validate-zone [
|
||
|
|
zone: string # Zone name
|
||
|
|
] -> record {
|
||
|
|
log debug $"Validating zone via API: ($zone)"
|
||
|
|
|
||
|
|
let result = call-dns-api $"/zones/($zone)/validate" "GET"
|
||
|
|
|
||
|
|
if $result.success {
|
||
|
|
$result.response
|
||
|
|
} else {
|
||
|
|
log error $"Failed to validate zone via API: ($result.error)"
|
||
|
|
{
|
||
|
|
valid: false
|
||
|
|
error: $result.error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|