@@ -8,11 +8,13 @@ Options:
88Optional:
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)
1112Issues: github.com/acmesh-official/acme.sh/issues
1213'
1314
1415HETZNERCLOUD_API_DEFAULT=" https://api.hetzner.cloud/v1"
1516HETZNERCLOUD_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