Skip to content

Commit 47d17c1

Browse files
authored
Merge pull request #1 from PKlapp/dns_hetznercloud_improvements
DNS Hetzner Cloud improvements
2 parents 3c3ec2c + 5f81460 commit 47d17c1

File tree

1 file changed

+163
-1
lines changed

1 file changed

+163
-1
lines changed

dnsapi/dns_hetznercloud.sh

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ Options:
88
Optional:
99
HETZNER_TTL Custom TTL for new TXT rrsets (default 120)
1010
HETZNER_API Override API endpoint (default https://api.hetzner.cloud/v1)
11+
HETZNER_MAX_ATTEMPTS Number of 1s polls to wait for async actions (default 120)
1112
Issues: github.com/acmesh-official/acme.sh/issues
1213
'
1314

1415
HETZNERCLOUD_API_DEFAULT="https://api.hetzner.cloud/v1"
1516
HETZNERCLOUD_TTL_DEFAULT=120
17+
HETZNER_MAX_ATTEMPTS_DEFAULT=120
1618

1719
######## Public functions #####################
1820

@@ -57,6 +59,9 @@ dns_hetznercloud_add() {
5759

5860
case "${_hetznercloud_last_http_code}" in
5961
200 | 201 | 202 | 204)
62+
if ! _hetznercloud_handle_action_response "TXT record add"; then
63+
return 1
64+
fi
6065
_info "Hetzner Cloud TXT record added."
6166
return 0
6267
;;
@@ -116,6 +121,9 @@ dns_hetznercloud_rm() {
116121
fi
117122
case "${_hetznercloud_last_http_code}" in
118123
200 | 201 | 202 | 204)
124+
if ! _hetznercloud_handle_action_response "TXT record remove"; then
125+
return 1
126+
fi
119127
_info "Hetzner Cloud TXT record removed."
120128
return 0
121129
;;
@@ -171,6 +179,17 @@ _hetznercloud_init() {
171179
fi
172180
_saveaccountconf_mutable HETZNER_TTL "${HETZNER_TTL}"
173181

182+
HETZNER_MAX_ATTEMPTS="${HETZNER_MAX_ATTEMPTS:-$(_readaccountconf_mutable HETZNER_MAX_ATTEMPTS)}"
183+
if [ -z "${HETZNER_MAX_ATTEMPTS}" ]; then
184+
HETZNER_MAX_ATTEMPTS="${HETZNER_MAX_ATTEMPTS_DEFAULT}"
185+
fi
186+
attempts_check=$(printf "%s" "${HETZNER_MAX_ATTEMPTS}" | tr -d '0-9')
187+
if [ -n "${attempts_check}" ]; then
188+
_err "HETZNER_MAX_ATTEMPTS must be an integer value."
189+
return 1
190+
fi
191+
_saveaccountconf_mutable HETZNER_MAX_ATTEMPTS "${HETZNER_MAX_ATTEMPTS}"
192+
174193
return 0
175194
}
176195

@@ -290,9 +309,15 @@ _hetznercloud_parse_zone_fields() {
290309
if [ -z "${zone_id}" ] || [ -z "${zone_name}" ]; then
291310
return 1
292311
fi
312+
zone_name_trimmed=$(printf "%s" "${zone_name}" | sed 's/\.$//')
313+
if zone_name_ascii=$(_idn "${zone_name_trimmed}"); then
314+
zone_name="${zone_name_ascii}"
315+
else
316+
zone_name="${zone_name_trimmed}"
317+
fi
293318
_hetznercloud_zone_id="${zone_id}"
294319
_hetznercloud_zone_name="${zone_name}"
295-
_hetznercloud_zone_name_lc=$(printf "%s" "${zone_name}" | sed 's/\.$//' | _lower_case)
320+
_hetznercloud_zone_name_lc=$(printf "%s" "${zone_name}" | _lower_case)
296321
return 0
297322
}
298323

@@ -429,3 +454,140 @@ _hetznercloud_api() {
429454

430455
return 0
431456
}
457+
458+
_hetznercloud_handle_action_response() {
459+
context="${1}"
460+
if [ -z "${response}" ]; then
461+
return 0
462+
fi
463+
464+
normalized=$(printf "%s" "${response}" | _normalizeJson)
465+
466+
failed_message=""
467+
if failed_message=$(_hetznercloud_extract_failed_action_message "${normalized}"); then
468+
if [ -n "${failed_message}" ]; then
469+
_err "Hetzner Cloud DNS ${context} failed: ${failed_message}"
470+
else
471+
_err "Hetzner Cloud DNS ${context} failed."
472+
fi
473+
return 1
474+
fi
475+
476+
action_ids=""
477+
if action_ids=$(_hetznercloud_extract_action_ids "${normalized}"); then
478+
for action_id in ${action_ids}; do
479+
if [ -z "${action_id}" ]; then
480+
continue
481+
fi
482+
if ! _hetznercloud_wait_for_action "${action_id}" "${context}"; then
483+
return 1
484+
fi
485+
done
486+
fi
487+
488+
return 0
489+
}
490+
491+
_hetznercloud_extract_failed_action_message() {
492+
normalized="${1}"
493+
failed_section=$(printf "%s" "${normalized}" | _egrep_o '"failed_actions":\[[^]]*\]')
494+
if [ -z "${failed_section}" ]; then
495+
return 1
496+
fi
497+
if _contains "${failed_section}" '"failed_actions":[]'; then
498+
return 1
499+
fi
500+
message=$(printf "%s" "${failed_section}" | _egrep_o '"message":"[^"]*"' | _head_n 1 | cut -d : -f 2 | tr -d '"')
501+
if [ -n "${message}" ]; then
502+
printf "%s" "${message}"
503+
else
504+
printf "%s" "${failed_section}"
505+
fi
506+
return 0
507+
}
508+
509+
_hetznercloud_extract_action_ids() {
510+
normalized="${1}"
511+
actions_section=$(printf "%s" "${normalized}" | _egrep_o '"actions":\[[^]]*\]')
512+
if [ -z "${actions_section}" ]; then
513+
return 1
514+
fi
515+
action_ids=$(printf "%s" "${actions_section}" | _egrep_o '"id":[0-9]*' | cut -d : -f 2 | tr -d '"' | tr '\n' ' ')
516+
action_ids=$(printf "%s" "${action_ids}" | tr -s ' ')
517+
action_ids=$(printf "%s" "${action_ids}" | sed 's/^ //;s/ $//')
518+
if [ -z "${action_ids}" ]; then
519+
return 1
520+
fi
521+
printf "%s" "${action_ids}"
522+
return 0
523+
}
524+
525+
_hetznercloud_wait_for_action() {
526+
action_id="${1}"
527+
context="${2}"
528+
attempts="0"
529+
530+
while true; do
531+
if ! _hetznercloud_api GET "/actions/${action_id}"; then
532+
return 1
533+
fi
534+
if [ "${_hetznercloud_last_http_code}" != "200" ]; then
535+
_hetznercloud_log_http_error "Hetzner Cloud DNS action ${action_id} query failed" "${_hetznercloud_last_http_code}"
536+
return 1
537+
fi
538+
539+
normalized=$(printf "%s" "${response}" | _normalizeJson)
540+
action_status=$(_hetznercloud_action_status_from_normalized "${normalized}")
541+
542+
if [ -z "${action_status}" ]; then
543+
_err "Hetzner Cloud DNS ${context} action ${action_id} returned no status."
544+
return 1
545+
fi
546+
547+
if [ "${action_status}" = "success" ]; then
548+
return 0
549+
fi
550+
551+
if [ "${action_status}" = "error" ]; then
552+
if action_error=$(_hetznercloud_action_error_from_normalized "${normalized}"); then
553+
_err "Hetzner Cloud DNS ${context} action ${action_id} failed: ${action_error}"
554+
else
555+
_err "Hetzner Cloud DNS ${context} action ${action_id} failed."
556+
fi
557+
return 1
558+
fi
559+
560+
attempts=$(_math "${attempts}" + 1)
561+
if [ "${attempts}" -ge "${HETZNER_MAX_ATTEMPTS}" ]; then
562+
_err "Hetzner Cloud DNS ${context} action ${action_id} did not complete after ${HETZNER_MAX_ATTEMPTS} attempts."
563+
return 1
564+
fi
565+
566+
_sleep 1
567+
done
568+
}
569+
570+
_hetznercloud_action_status_from_normalized() {
571+
normalized="${1}"
572+
status=$(printf "%s" "${normalized}" | _egrep_o '"status":"[^"]*"' | _head_n 1 | cut -d : -f 2 | tr -d '"')
573+
printf "%s" "${status}"
574+
}
575+
576+
_hetznercloud_action_error_from_normalized() {
577+
normalized="${1}"
578+
error_section=$(printf "%s" "${normalized}" | _egrep_o '"error":{[^}]*}')
579+
if [ -z "${error_section}" ]; then
580+
return 1
581+
fi
582+
message=$(printf "%s" "${error_section}" | _egrep_o '"message":"[^"]*"' | _head_n 1 | cut -d : -f 2 | tr -d '"')
583+
if [ -n "${message}" ]; then
584+
printf "%s" "${message}"
585+
return 0
586+
fi
587+
code=$(printf "%s" "${error_section}" | _egrep_o '"code":"[^"]*"' | _head_n 1 | cut -d : -f 2 | tr -d '"')
588+
if [ -n "${code}" ]; then
589+
printf "%s" "${code}"
590+
return 0
591+
fi
592+
return 1
593+
}

0 commit comments

Comments
 (0)