You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
7.4 KiB

  1. # -*- mode: shell-script -*-
  2. cron:get_config() {
  3. local cfg="$1"
  4. local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \
  5. type value
  6. if [ -e "$cache_file" ]; then
  7. #debug "$FUNCNAME: SESSION cache hit $1"
  8. cat "$cache_file"
  9. return 0
  10. fi
  11. type=$(e "$cfg" | shyaml -q get-type 2>/dev/null) || true
  12. case "$type" in
  13. "sequence")
  14. while read-0-err E s; do
  15. cron:get_config "$s" || return 1
  16. done < <(e "$cfg" | p-err shyaml -q get-values-0 -y)
  17. if [ "$E" != 0 ]; then
  18. err "Failed to parse sequence while reading config."
  19. return 1
  20. fi
  21. ;;
  22. "struct")
  23. while read-0-err E k v; do
  24. while read-0-err E1 schedule lock_opts title command; do
  25. if [ -n "$title" ]; then
  26. err "Unexpected label specified in struct."
  27. echo " Using struct, the key will be used as label." >&2
  28. echo " So you can't specify a label inner value(s)." >&2
  29. return 1
  30. fi
  31. p0 "$schedule" "$lock_opts" "$k" "$command"
  32. done < <(p-err cron:get_config "$v")
  33. if [ "$E1" != 0 ]; then
  34. err "Failed to parse value of key '$k' in struct config."
  35. return 1
  36. fi
  37. done < <(e "$cfg" | p-err shyaml -q key-values-0 -y)
  38. if [ "$E" != 0 ]; then
  39. err "Failed to parse key values while reading config."
  40. return 1
  41. fi
  42. ;;
  43. "str")
  44. ## examples:
  45. ## (*/5 * * * *) {-k} bash -c "foo bla bla"
  46. ## (@daily) {-p 10 -D} bash -c "foo bla bla"
  47. value=$(e "$cfg" | yaml_get_values) || {
  48. err "Failed to parse str while reading config."
  49. return 1
  50. }
  51. if ! [[ "$value" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)?[[:space:]]*"("([^\)]+)")"[[:space:]]+\{([^\}]*)\}[[:space:]]*(.*)$ ]]; then
  52. err "Invalid syntax, expected: 'LABEL (SCHEDULE) {LOCK_OPTIONS} COMMAND'."
  53. echo " With LABEL being a possible empty string." >&2
  54. echo " Received: '$value'" >&2
  55. return 1
  56. fi
  57. printf "%s\0" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[1]}" "${BASH_REMATCH[4]}"
  58. ;;
  59. NoneType|"")
  60. :
  61. ;;
  62. *)
  63. value=$(e "$cfg" | yaml_get_interpret) || {
  64. err "Failed to parse value while reading config."
  65. return 1
  66. }
  67. if [[ "$value" == "$cfg" ]]; then
  68. err "Unrecognized type '$type'."
  69. return 1
  70. fi
  71. cron:get_config "$value" || return 1
  72. ;;
  73. esac > "$cache_file.tmp"
  74. mv -v "$cache_file.tmp" "$cache_file" >&2
  75. ## if cache file is empty, this is an error
  76. if [ ! -s "$cache_file" ]; then
  77. err "Unexpected empty relation options."
  78. echo " - check that you don't overwrite default options with an empty relation" >&2
  79. echo " - check your charm is setting default options" >&2
  80. echo " Original value: '$cfg'" >&2
  81. rm -f "$cache_file"
  82. return 1
  83. fi
  84. cat "$cache_file"
  85. }
  86. cron:entries_from_service() {
  87. local service="$1" relation_cfg="$2" \
  88. cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \
  89. label schedule lock_opts title command full_label
  90. if [ -e "$cache_file" ]; then
  91. #debug "$FUNCNAME: SESSION cache hit $1"
  92. cat "$cache_file"
  93. return 0
  94. fi
  95. ## XXXvlab; should factorize this with compose-core to setup relation env
  96. export BASE_SERVICE_NAME=$service
  97. MASTER_BASE_SERVICE_NAME=$(get_top_master_service_for_service "$service") || return 1
  98. MASTER_BASE_CHARM_NAME=$(get_service_charm "$MASTER_BASE_SERVICE_NAME") || return 1
  99. BASE_CHARM_NAME=$(get_service_charm "$service") || return 1
  100. BASE_CHARM_PATH=$(charm.get_dir "$BASE_CHARM_NAME") || return 1
  101. export MASTER_BASE_{CHARM,SERVICE}_NAME BASE_CHARM_{PATH,NAME}
  102. label="launch-$service"
  103. while read-0-err E schedule lock_opts title command; do
  104. lock_opts=($lock_opts)
  105. if ! [[ "$schedule" =~ ^(([0-9/,*-]+[[:space:]]+){4,4}[0-9/,*-]+|@[a-z]+)$ ]]; then
  106. err "Unrecognized schedule '$schedule'."
  107. return 1
  108. fi
  109. ## Check that label is only a simple identifier
  110. if ! [[ "$title" =~ ^[a-zA-Z0-9_-]*$ ]]; then
  111. err "Unexpected title '$title', please use only alphanumeric, underscore or dashes (can be empty)."
  112. return 1
  113. fi
  114. if ! lock_opts=($(cron:lock_opts "${lock_opts[@]}")); then
  115. err "Failed to parse lock options."
  116. return 1
  117. fi
  118. if [ -z "$command" ]; then
  119. err "Unexpected empty command."
  120. return 1
  121. fi
  122. full_label="$label"
  123. [ -n "$title" ] && full_label+="-$title"
  124. ## escape double-quotes
  125. command=${command//\"/\\\"}
  126. p0 "$schedule lock ${full_label} ${lock_opts[*]} -c \"$command\" 2>&1 | awk '{ print strftime(\"%Y-%m-%d %H:%M:%S %Z\"), \$0; fflush(); }' >> /var/log/cron/${full_label}_script.log"
  127. done < <(p-err cron:get_config "$relation_cfg") > "$cache_file"
  128. if [ "$E" != 0 ]; then
  129. rm -f "$cache_file"
  130. err "Failed to get ${DARKYELLOW}$service${NORMAL}--${DARKBLUE}schedule-command${NORMAL}-->${DARKYELLOW}$SERVICE_NAME${NORMAL}'s config."
  131. return 1
  132. fi
  133. cat "$cache_file"
  134. }
  135. cron:lock_opts() {
  136. local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \
  137. label schedule lock_opts title command full_label
  138. if [ -e "$cache_file" ]; then
  139. #debug "$FUNCNAME: SESSION cache hit $1"
  140. cat "$cache_file"
  141. return 0
  142. fi
  143. lock_opts=()
  144. while [ "$1" ]; do
  145. case "$1" in
  146. "-D"|"-k")
  147. lock_opts+=("$1")
  148. ;;
  149. "-p")
  150. ## check that the value is a number
  151. if ! [[ "$2" =~ ^[0-9]+$ ]]; then
  152. err "Unexpected value for priority '$2' (expected an integer)."
  153. return 1
  154. fi
  155. lock_opts+=(-p "$2")
  156. shift
  157. ;;
  158. "-*"|"--*")
  159. err "Unrecognized lock option '$1'."
  160. return 1
  161. ;;
  162. *)
  163. err "Unexpected lock argument '$1'."
  164. return 1
  165. ;;
  166. esac
  167. shift
  168. done
  169. printf "%s\n" "${lock_opts[@]}" > "$cache_file"
  170. cat "$cache_file"
  171. }
  172. cron:entries() {
  173. local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$ALL_RELATIONS")" \
  174. s rn ts rc td
  175. if [ -e "$cache_file" ]; then
  176. #debug "$FUNCNAME: SESSION cache hit $1"
  177. cat "$cache_file"
  178. return 0
  179. fi
  180. if [ -z "$ALL_RELATIONS" ]; then
  181. err "Expected \$ALL_RELATIONS to be set."
  182. exit 1
  183. fi
  184. export TARGET_SERVICE_NAME=$SERVICE_NAME
  185. while read-0 service relation_cfg; do
  186. debug "service: '$service' relation_cfg: '$relation_cfg'"
  187. cron:entries_from_service "$service" "$relation_cfg" || return 1
  188. done < <(get_service_incoming_relations "$SERVICE_NAME" "schedule-command") > "$cache_file"
  189. cat "$cache_file"
  190. }
  191. export -f cron:entries