Skip to content

Commit 8070654

Browse files
committed
feat(ct): add database settings replacement script to configbaker image #11639
- Introduced `apply-db-settings.sh` for safely replacing database settings in a Dataverse instance. - Added logic to validate inputs, ensure dependencies (`yq`, `jq`, `wait4x`) are available, and handle API authentication via unblock keys. - Updated Dockerfile to include `yq` installation with specified version.
1 parent c833864 commit 8070654

2 files changed

Lines changed: 137 additions & 2 deletions

File tree

modules/container-configbaker/Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ ENV PATH="${PATH}:${SCRIPT_DIR}" \
2323
ARG PKGS="curl dnsutils dumb-init ed jq netcat-openbsd postgresql-client"
2424
# renovate: datasource=github-releases depName=wait4x/wait4x
2525
ARG WAIT4X_VERSION="v3.2.0"
26+
# renove: datasource=github-releases depName=mikefarah/yq
27+
ARG YQ_VERSION="v4.47.1"
2628
# renovate: datasource=pypi depName=awscli
2729
ARG AWSCLI_VERSION="1.40.15"
2830
ARG PYTHON_PKGS="awscli==${AWSCLI_VERSION}"
@@ -65,7 +67,11 @@ RUN true && \
6567
echo "$(cat /tmp/w4x-checksum | cut -f1 -d" ") /usr/bin/wait4x.tar.gz" | sha256sum -c - && \
6668
tar -xzf /usr/bin/wait4x.tar.gz -C /usr/bin && chmod +x /usr/bin/wait4x && \
6769

68-
# 2. Python packages
70+
# 2. yq-go \
71+
curl -sSfL -o /usr/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_${ARCH}" && \
72+
chmod +x /usr/bin/yq && \
73+
74+
# 3. Python packages
6975
pipx install --global ${PYTHON_PKGS}
7076

7177
# Get in the scripts
@@ -81,7 +87,7 @@ COPY --from=solr /opt/solr/server/solr/configsets/_default ${SOLR_TEMPLATE}/
8187
COPY maven/solr/*.xml ${SOLR_TEMPLATE}/conf/
8288
RUN rm ${SOLR_TEMPLATE}/conf/managed-schema.xml
8389

84-
90+
WORKDIR ${SCRIPT_DIR}
8591
# Set the entrypoint to tini (as a process supervisor)
8692
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
8793
# By default run a script that will print a help message and terminate
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/env bash
2+
3+
# [INFO]: Idempotent replacement of all database settings from a file source.
4+
5+
set -euo pipefail
6+
7+
function usage() {
8+
echo "Usage: $(basename "$0") [-h] [-u instanceUrl] [-t timeout] [-c configFile] [-b unblockKey]"
9+
echo ""
10+
echo "Replace all Database Settings in a running Dataverse installation in an idempotent way."
11+
echo ""
12+
echo "Parameters:"
13+
echo "instanceUrl - Location on container network where to reach your instance. Default: 'http://dataverse:8080'"
14+
echo " Can be set as environment variable 'DATAVERSE_URL'."
15+
echo " timeout - Provide how long to wait for the instance to become available (using wait4x). Default: '3m'"
16+
echo " Can be set as environment variable 'TIMEOUT'."
17+
echo " configFile - Path to a JSON, YAML, PROPERTIES or TOML file containing your settings. Default: '/dv/db-opts.yml'"
18+
echo " Can be set as environment variable 'CONFIG_FILE'."
19+
echo " unblockKey - Either string or path to a file with the Admin API Unblock Key. Optional for localhost. No default."
20+
echo " Can be set as environment variable 'ADMIN_API_UNBLOCK_KEY'."
21+
echo ""
22+
echo "Note: This script will wait for the Dataverse instance to be available before executing the replacement."
23+
echo " Be careful - this script will not stop you from deleting any vital settings."
24+
echo ""
25+
exit 1
26+
}
27+
28+
### Common functions
29+
function error {
30+
echo "ERROR:" "$@" >&2
31+
exit 2
32+
}
33+
34+
function exists {
35+
type "$1" >/dev/null 2>&1 && return 0
36+
( IFS=:; for p in $PATH; do [ -x "${p%/}/$1" ] && return 0; done; return 1 )
37+
}
38+
39+
# Check for (the right) yq, jq, and wait4x being available
40+
if ! exists yq; then
41+
error "No yq executable found on PATH."
42+
elif ! grep -q "https://github.com/mikefarah/yq" <((yq --version)); then
43+
error "You must install yq from https://github.com/mikefarah/yq, not https://github.com/kislyuk/yq"
44+
fi
45+
if ! exists jq; then
46+
error "No jq executable found on PATH."
47+
fi
48+
if ! exists wait4x; then
49+
error "No wait4x executable found on PATH."
50+
fi
51+
52+
# Set some defaults as documented
53+
DATAVERSE_URL=${DATAVERSE_URL:-"http://dataverse:8080"}
54+
ADMIN_API_UNBLOCK_KEY=${ADMIN_API_UNBLOCK_KEY:-""}
55+
TIMEOUT=${TIMEOUT:-"3m"}
56+
CONFIG_FILE=${CONFIG_FILE:-"/dv/db-opts.yml"}
57+
58+
while getopts "u:t:c:b:h" OPTION
59+
do
60+
case "$OPTION" in
61+
u) DATAVERSE_URL="$OPTARG" ;;
62+
t) TIMEOUT="$OPTARG" ;;
63+
c) CONFIG_FILE="$OPTARG" ;;
64+
b) ADMIN_API_UNBLOCK_KEY="$OPTARG" ;;
65+
h) usage;;
66+
\?) usage;;
67+
esac
68+
done
69+
shift $((OPTIND-1))
70+
71+
# Define an auth header argument (enabling usage of different ways)
72+
AUTH_HEADER_ARG=""
73+
74+
# Check for Dataverse Unblock API Key present (option with file/env var)
75+
# This is only required if the host is not localhost (then there may be no key necessary)
76+
if ! [[ "${DATAVERSE_URL}" == *"://localhost"* ]] || [ -n "${ADMIN_API_UNBLOCK_KEY}" ]; then
77+
# The argument should not be empty
78+
if [ -z "${ADMIN_API_UNBLOCK_KEY}" ]; then
79+
error "You must provide the Dataverse API Unblock Key to this script."
80+
# In case it's not empty, check if it's a file path and read the key from there
81+
elif [ -f "${ADMIN_API_UNBLOCK_KEY}" ] && [ -r "${ADMIN_API_UNBLOCK_KEY}" ]; then
82+
echo "Reading Dataverse API Unblock Key from ${ADMIN_API_UNBLOCK_KEY}."
83+
if ! API_KEY_FILE_CONTENT=$(cat "${ADMIN_API_UNBLOCK_KEY}" 2>/dev/null); then
84+
error "Could not read unblock key from file ${ADMIN_API_UNBLOCK_KEY}."
85+
fi
86+
# Validate the key is not empty
87+
if [ -z "${API_KEY_FILE_CONTENT}" ]; then
88+
error "API key file ${ADMIN_API_UNBLOCK_KEY} appears empty."
89+
fi
90+
ADMIN_API_UNBLOCK_KEY="$API_KEY_CONTENT"
91+
fi
92+
# Very basic error check (as there is no clear format or formal spec for the key)
93+
if [ ${#ADMIN_API_UNBLOCK_KEY} -lt 5 ]; then
94+
error "API key appears to be too short (<5 chars)."
95+
fi
96+
97+
# Build the header argument for Admin API Authentication via unblock key
98+
AUTH_HEADER_ARG="X-Dataverse-unblock-key: ${ADMIN_API_UNBLOCK_KEY}"
99+
fi
100+
101+
# Check for file with DB options given, file present and readable as well as parseable by yq
102+
# If parseable, render as JSON to temp file
103+
CONV_CONF_FILE=$(mktemp)
104+
if [ -f "${CONFIG_FILE}" ] && [ -r "${CONFIG_FILE}" ]; then
105+
yq -M -o json "${CONFIG_FILE}" > "${CONV_CONF_FILE}" || error "Could not parse config file with yq from ${CONFIG_FILE}."
106+
else
107+
error "Could not read a config file at ${CONFIG_FILE}."
108+
fi
109+
110+
# Check or wait for Dataverse API being responsive
111+
echo "Waiting for ${DATAVERSE_URL} to become ready in max ${TIMEOUT}."
112+
wait4x http "${DATAVERSE_URL}/api/info/version" -i 8s -t "$TIMEOUT" --expect-status-code 200 --expect-body-json data.version
113+
114+
# Check for Dataverse Admin API endpoints being reachable by retrieving the current DB options, expect blockades!
115+
CURRENT_SETTINGS=$(mktemp)
116+
echo "Retrieving settings from running instance."
117+
# TODO: Do we need to support pre v6.7 style unblock key query parameter?
118+
curl -sSL --fail-with-body -o "${CURRENT_SETTINGS}" -H "${AUTH_HEADER_ARG}" "${DATAVERSE_URL}/api/admin/settings" \
119+
|| error "Failed. Response message: $( cat "${CURRENT_SETTINGS}")" \
120+
&& echo "Success!"
121+
# TODO: while it's nice to have the current settings written out, it may contain sensitive information (so don't).
122+
# && ( echo "Success! Current settings: "; jq '.data' < "$CURRENT_SETTINGS" )
123+
124+
# We need to make the settings update atomic.
125+
echo "Replacing settings."
126+
RESPONSE=$(mktemp)
127+
curl -sSL --fail-with-body -o "${RESPONSE}" -X PUT -H "${AUTH_HEADER_ARG}" --json @"${CONV_CONF_FILE}" "${DATAVERSE_URL}/api/admin/settings" \
128+
|| error "Failed. Response message: $( jq ".message" < "${RESPONSE}" )" \
129+
&& ( echo -e "Success!\nOperations executed: "; jq '.data' < "$RESPONSE" )

0 commit comments

Comments
 (0)