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.

235 lines
8.2 KiB

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