# -*- mode: shell-script -*- cron:get_config() { local cfg="$1" local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$@")" \ type value 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" || 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") 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" || return 1 ;; esac > "$cache_file" ## 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 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} 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") > "$cache_file" 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 command full_label 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() { local cache_file="$CACHEDIR/$FUNCNAME.cache.$(H "$SERVICE_NAME" "$ALL_RELATIONS")" \ s rn ts rc td if [ -e "$cache_file" ]; then #debug "$FUNCNAME: SESSION cache hit $1" cat "$cache_file" return 0 fi if [ -z "$ALL_RELATIONS" ]; then err "Expected \$ALL_RELATIONS to be set." exit 1 fi export TARGET_SERVICE_NAME=$SERVICE_NAME while read-0 service relation_cfg; do debug "service: '$service' relation_cfg: '$relation_cfg'" cron:entries_from_service "$service" "$relation_cfg" || return 1 done < <(get_service_incoming_relations "$SERVICE_NAME" "schedule-command") > "$cache_file" cat "$cache_file" } export -f cron:entries