# -*- mode: shell-script -*- cron:get_config() { local cfg="$1" env_hash="$2" local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \ type value local k v E E1 title command lock_opts schedule if [ -e "$cache_file" ]; then debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" return 0 fi type=$(e "$cfg" | shyaml -q get-type 2>/dev/null) || true case "$type" in "sequence") while read-0-err E s; do cron:get_config "$s" "$env_hash" || return 1 done < <(e "$cfg" | p-err shyaml -q get-values-0 -y) if [ "$E" != 0 ]; then err "Failed to parse sequence while reading config." return 1 fi ;; "struct") while read-0-err E k v; do while read-0-err E1 schedule lock_opts title command; do if [ -n "$title" ]; then err "Unexpected label specified in struct." echo " Using struct, the key will be used as label." >&2 echo " So you can't specify a label inner value(s)." >&2 return 1 fi p0 "$schedule" "$lock_opts" "$k" "$command" done < <(p-err cron:get_config "$v" "$env_hash") if [ "$E1" != 0 ]; then err "Failed to parse value of key '$k' in struct config." return 1 fi done < <(e "$cfg" | p-err shyaml -q key-values-0 -y) if [ "$E" != 0 ]; then err "Failed to parse key values while reading config." return 1 fi ;; "str") ## examples: ## (*/5 * * * *) {-k} bash -c "foo bla bla" ## (@daily) {-p 10 -D} bash -c "foo bla bla" value=$(e "$cfg" | yaml_get_values) || { err "Failed to parse str while reading config." return 1 } if ! [[ "$value" =~ ^[[:space:]]*([a-zA-Z0-9_-]+)?[[:space:]]*"("([^\)]+)")"[[:space:]]+\{([^\}]*)\}[[:space:]]*(.*)$ ]]; then err "Invalid syntax, expected: 'LABEL (SCHEDULE) {LOCK_OPTIONS} COMMAND'." echo " With LABEL being a possible empty string." >&2 echo " Received: '$value'" >&2 return 1 fi printf "%s\0" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}" "${BASH_REMATCH[1]}" "${BASH_REMATCH[4]}" ;; NoneType|"") : ;; *) value=$(e "$cfg" | yaml_get_interpret) || { err "Failed to parse value while reading config." return 1 } if [[ "$value" == "$cfg" ]]; then err "Unrecognized type '$type'." return 1 fi cron:get_config "$value" "$env_hash" || return 1 ;; esac > "$cache_file.tmp" mv "$cache_file.tmp" "$cache_file" >&2 ## if cache file is empty, this is an error if [ ! -s "$cache_file" ]; then err "Unexpected empty relation options." echo " - check that you don't overwrite default options with an empty relation" >&2 echo " - check your charm is setting default options" >&2 echo " Original value: '$cfg'" >&2 rm -f "$cache_file" return 1 fi cat "$cache_file" } cron:entries_from_service() { local service="$1" relation_cfg="$2" \ cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \ label schedule lock_opts title command full_label H if [ -e "$cache_file" ]; then debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" return 0 fi ## XXXvlab; should factorize this with compose-core to setup relation env export BASE_SERVICE_NAME=$service MASTER_BASE_SERVICE_NAME=$(get_top_master_service_for_service "$service") || return 1 MASTER_BASE_CHARM_NAME=$(get_service_charm "$MASTER_BASE_SERVICE_NAME") || return 1 BASE_CHARM_NAME=$(get_service_charm "$service") || return 1 BASE_CHARM_PATH=$(charm.get_dir "$BASE_CHARM_NAME") || return 1 export MASTER_BASE_{CHARM,SERVICE}_NAME BASE_CHARM_{PATH,NAME} H=$( env_vars=(MASTER_BASE_{CHARM,SERVICE}_NAME BASE_CHARM_{PATH,NAME} BASE_SERVICE_NAME) values=() for v in "${env_vars[@]}"; do values+=("${!v}") done H "${values[@]}" ) label="launch-$service" while read-0-err E schedule lock_opts title command; do lock_opts=($lock_opts) if ! [[ "$schedule" =~ ^(([0-9/,*-]+[[:space:]]+){4,4}[0-9/,*-]+|@[a-z]+)$ ]]; then err "Unrecognized schedule '$schedule'." return 1 fi ## Check that label is only a simple identifier if ! [[ "$title" =~ ^[a-zA-Z0-9_-]*$ ]]; then err "Unexpected title '$title', please use only alphanumeric, underscore or dashes (can be empty)." return 1 fi if ! lock_opts=($(cron:lock_opts "${lock_opts[@]}")); then err "Failed to parse lock options." return 1 fi if [ -z "$command" ]; then err "Unexpected empty command." return 1 fi full_label="$label" [ -n "$title" ] && full_label+="-$title" ## escape double-quotes command=${command//\"/\\\"} 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" done < <(p-err cron:get_config "$relation_cfg" "$H") > "$cache_file.tmp" mv "$cache_file.tmp" "$cache_file" >&2 if [ "$E" != 0 ]; then rm -f "$cache_file" err "Failed to get ${DARKYELLOW}$service${NORMAL}--${DARKBLUE}schedule-command${NORMAL}-->${DARKYELLOW}$SERVICE_NAME${NORMAL}'s config." return 1 fi cat "$cache_file" } cron:lock_opts() { local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \ label schedule lock_opts title if [ -e "$cache_file" ]; then #debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" return 0 fi lock_opts=() while [ "$1" ]; do case "$1" in "-D"|"-k") lock_opts+=("$1") ;; "-p") ## check that the value is a number if ! [[ "$2" =~ ^[0-9]+$ ]]; then err "Unexpected value for priority '$2' (expected an integer)." return 1 fi lock_opts+=(-p "$2") shift ;; "-*"|"--*") err "Unrecognized lock option '$1'." return 1 ;; *) err "Unexpected lock argument '$1'." return 1 ;; esac shift done printf "%s\n" "${lock_opts[@]}" > "$cache_file" cat "$cache_file" } cron:entries() { if [ -z "$SUBSET_ALL_RELATIONS_HASH" ]; then err "Expected \$SUBSET_ALL_RELATIONS_HASH to be set." return 1 fi local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$SUBSET_ALL_RELATIONS_HASH")" local s rn ts rc td E if [ -e "$cache_file" ]; then #debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" return 0 fi export TARGET_SERVICE_NAME="$SERVICE_NAME" while read-0-err E service relation_cfg; do debug "service: '$service' relation_cfg:" >&2 e "$relation_cfg" | prefix " | " >&2 echo "" >&2 cron:entries_from_service "$service" "$relation_cfg" || { err-d "Failed to get entries from service '$service'." return 1 } done < <(p-err get_service_incoming_relations "$SERVICE_NAME" "schedule-command") > "$cache_file.tmp" if [ "$E" != 0 ]; then rm -f "$cache_file.tmp" err "Failed to get ${DARKYELLOW}*${NORMAL}--${DARKBLUE}schedule-command${NORMAL}-->${DARKYELLOW}$SERVICE_NAME${NORMAL}'s relations." return 1 fi mv "$cache_file"{.tmp,} >&2 cat "$cache_file" } export -f cron:entries