diff --git a/postgres-stub/README.org b/postgres-stub/README.org index 88e8fd4..37cc56d 100644 --- a/postgres-stub/README.org +++ b/postgres-stub/README.org @@ -44,3 +44,20 @@ myservice: #+end_src +** database name and user automatic assignation + +It can be useful, if you have several services in the same ~compose.yml~ +to assign database names and user automatically to avoid repeating them. + +#+begin_src yaml +my-postgres-stub: + charm: postgres-stub + options: + host: my-host.mydomain.fr:20432 + service-dbname-map: + service1: u_foo + ## Leveraging regex replacement for all other services + .*: xyz_$0 + service-user-map: + .*: u_$0 +#+end_src diff --git a/postgres-stub/hooks/postgres_database-relation-joined b/postgres-stub/hooks/postgres_database-relation-joined index dbbb96e..04ae610 100755 --- a/postgres-stub/hooks/postgres_database-relation-joined +++ b/postgres-stub/hooks/postgres_database-relation-joined @@ -7,14 +7,19 @@ ## - the target of the link is launched first, and get a chance to ``relation-set`` ## - both side of the scripts get to use ``relation-get``. +. lib/common + +set -e DBNAME=$(relation-get dbname) || { - DBNAME="$BASE_SERVICE_NAME" + DBNAME=$(service:map:get_one "dbname") || exit 1 + DBNAME="${DBNAME:-$BASE_SERVICE_NAME}" relation-set dbname "$DBNAME" } USER=$(relation-get user) || { - USER="$BASE_SERVICE_NAME" + USER=$(service:map:get_one "user") || exit 1 + USER="${USER:-$BASE_SERVICE_NAME}" relation-set user "$USER" } @@ -41,8 +46,6 @@ PORT=${PORT:-5432} relation-set host "$HOST" relation-set port "$PORT" -. lib/common - set -e if ! PASSWORD="$(relation-get password 2>/dev/null)"; then diff --git a/postgres-stub/lib/common b/postgres-stub/lib/common index 73c2af2..93af1ee 100644 --- a/postgres-stub/lib/common +++ b/postgres-stub/lib/common @@ -3,6 +3,92 @@ POSTGRES_IMAGE=docker.0k.io/postgres:12.15.0-myc +## Mappings + +replace_by_rematch_pattern() { + local input="$1" output="" char next_char i + # Loop through each character in the string + for (( i=0; i<${#input}; i++ )); do + char="${input:$i:1}" + + # If a dollar sign is found + if [[ "$char" == '$' ]]; then + next_char="${input:$((i+1)):1}" + # Check if next character is a digit + if [[ "$next_char" =~ [0-9] ]]; then + # Replace $N with ${rematch[N]} + output+='${rematch['"$next_char"']}' + ((i++)) # Skip next character as it's already processed + continue + fi + fi + + output+="$char" + done + + echo "$output" +} +export -f replace_by_rematch_pattern + + +service:map:get_one() { + local map_label="$1" + res=() + if service_map=$(options-get "service-${map_label}-map" 2>/dev/null) && + [ -n "$service_map" ]; then + while read-0 regex map; do + if [[ "$BASE_SERVICE_NAME" =~ ^$regex$ ]]; then + rematch=("${BASH_REMATCH[@]}") + maps=() + type=$(e "$map" | shyaml -q get-type 2>/dev/null) || true + value=$(e "$map" | shyaml -q get-value -y 2>/dev/null) || true + case "$type" in + sequence) + while read-0 m; do + if [[ "$m" != "None" ]] && [ -n "$m" ]; then + maps+=("$m") + fi + done < <(e "$value" | shyaml get-values-0) + ;; + str) + if ! m=$(e "$value" | shyaml get-value 2>/dev/null); then + err "Failed to get mapping value from config." + return 1 + fi + [ -z "$m" ] && continue + maps+=("$m") + ;; + NoneType|"") + : + ;; + esac + for map in "${maps[@]}"; do + if ! [[ "$map" =~ ^([a-zA-Z0-9_*\{\}\ \,.-]|\$[0-9])+$ ]]; then + err "Invalid mapping value '$map' in ${WHITE}service-${map_label}-map$NORMAL option." + return 1 + fi + + map="${map//\*/\\*}" ## protect star from eval + map=$(replace_by_rematch_pattern "$map") + + res+=($(set -- "${BASH_REMATCH[@]}"; eval echo "${map//\*/\\*}")) + done + break + fi + done < <(e "$service_map" | yq e -0 'to_entries | .[] | [.key, .value] |.[]') + fi + if [ "${#res[@]}" -gt 1 ]; then + err "Multiple databases found for service '$BASE_SERVICE_NAME': ${dbnames[*]}." \ + "Please specify a single database in the service-${map_label}-map option." + return 1 + fi + echo "${res[0]}" +} + +## +## DB +## + ddb () { docker run --rm -i \ -e PGPASSWORD="$PASSWORD" \