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.

207 lines
7.3 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"
  74. ## if cache file is empty, this is an error
  75. if [ ! -s "$cache_file" ]; then
  76. err "Unexpected empty relation options."
  77. echo " - check that you don't overwrite default options with an empty relation" >&2
  78. echo " - check your charm is setting default options" >&2
  79. echo " Original value: '$cfg'" >&2
  80. rm -f "$cache_file"
  81. return 1
  82. fi
  83. cat "$cache_file"
  84. }
  85. cron:entries_from_service() {
  86. local service="$1" relation_cfg="$2" \
  87. cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \
  88. label schedule lock_opts title command full_label
  89. if [ -e "$cache_file" ]; then
  90. #debug "$FUNCNAME: SESSION cache hit $1"
  91. cat "$cache_file"
  92. return 0
  93. fi
  94. ## XXXvlab; should factorize this with compose-core to setup relation env
  95. export BASE_SERVICE_NAME=$service
  96. MASTER_BASE_SERVICE_NAME=$(get_top_master_service_for_service "$service") || return 1
  97. MASTER_BASE_CHARM_NAME=$(get_service_charm "$MASTER_BASE_SERVICE_NAME") || return 1
  98. BASE_CHARM_NAME=$(get_service_charm "$service") || return 1
  99. BASE_CHARM_PATH=$(charm.get_dir "$BASE_CHARM_NAME") || return 1
  100. export MASTER_BASE_{CHARM,SERVICE}_NAME BASE_CHARM_{PATH,NAME}
  101. label="launch-$service"
  102. while read-0-err E schedule lock_opts title command; do
  103. lock_opts=($lock_opts)
  104. if ! [[ "$schedule" =~ ^(([0-9/,*-]+[[:space:]]+){4,4}[0-9/,*-]+|@[a-z]+)$ ]]; then
  105. err "Unrecognized schedule '$schedule'."
  106. return 1
  107. fi
  108. ## Check that label is only a simple identifier
  109. if ! [[ "$title" =~ ^[a-zA-Z0-9_-]*$ ]]; then
  110. err "Unexpected title '$title', please use only alphanumeric, underscore or dashes (can be empty)."
  111. return 1
  112. fi
  113. if ! lock_opts=($(cron:lock_opts "${lock_opts[@]}")); then
  114. err "Failed to parse lock options."
  115. return 1
  116. fi
  117. if [ -z "$command" ]; then
  118. err "Unexpected empty command."
  119. return 1
  120. fi
  121. full_label="$label"
  122. [ -n "$title" ] && full_label+="-$title"
  123. ## escape double-quotes
  124. command=${command//\"/\\\"}
  125. 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"
  126. done < <(p-err cron:get_config "$relation_cfg") > "$cache_file"
  127. if [ "$E" != 0 ]; then
  128. rm -f "$cache_file"
  129. err "Failed to get ${DARKYELLOW}$service${NORMAL}--${DARKBLUE}schedule-command${NORMAL}-->${DARKYELLOW}$SERVICE_NAME${NORMAL}'s config."
  130. return 1
  131. fi
  132. cat "$cache_file"
  133. }
  134. cron:lock_opts() {
  135. local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \
  136. label schedule lock_opts title command full_label
  137. if [ -e "$cache_file" ]; then
  138. #debug "$FUNCNAME: SESSION cache hit $1"
  139. cat "$cache_file"
  140. return 0
  141. fi
  142. lock_opts=()
  143. while [ "$1" ]; do
  144. case "$1" in
  145. "-D"|"-k")
  146. lock_opts+=("$1")
  147. ;;
  148. "-p")
  149. ## check that the value is a number
  150. if ! [[ "$2" =~ ^[0-9]+$ ]]; then
  151. err "Unexpected value for priority '$2' (expected an integer)."
  152. return 1
  153. fi
  154. lock_opts+=(-p "$2")
  155. shift
  156. ;;
  157. "-*"|"--*")
  158. err "Unrecognized lock option '$1'."
  159. return 1
  160. ;;
  161. *)
  162. err "Unexpected lock argument '$1'."
  163. return 1
  164. ;;
  165. esac
  166. shift
  167. done
  168. printf "%s\n" "${lock_opts[@]}" > "$cache_file"
  169. cat "$cache_file"
  170. }
  171. cron:entries() {
  172. local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$ALL_RELATIONS")" \
  173. s rn ts rc td
  174. if [ -e "$cache_file" ]; then
  175. #debug "$FUNCNAME: SESSION cache hit $1"
  176. cat "$cache_file"
  177. return 0
  178. fi
  179. if [ -z "$ALL_RELATIONS" ]; then
  180. err "Expected \$ALL_RELATIONS to be set."
  181. exit 1
  182. fi
  183. export TARGET_SERVICE_NAME=$SERVICE_NAME
  184. while read-0 service relation_cfg; do
  185. debug "service: '$service' relation_cfg: '$relation_cfg'"
  186. cron:entries_from_service "$service" "$relation_cfg" || return 1
  187. done < <(get_service_incoming_relations "$SERVICE_NAME" "schedule-command") > "$cache_file"
  188. cat "$cache_file"
  189. }
  190. export -f cron:entries