Merge pull request #180 from darkfronza/PG-342_api_check_script
PG-342: Add API compatibility verification script.pull/181/head
commit
855967d915
|
@ -0,0 +1,278 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# api_check.sh: Check API compatibility between two pg_stat_monitor branches.
|
||||
#
|
||||
# Usage: api_check.sh [source_branch] target_branch
|
||||
# OR
|
||||
# api_check.sh clean
|
||||
#
|
||||
# source_branch is optional, if omitted the script will use the current git branch
|
||||
# as the source.
|
||||
#
|
||||
# If invoked as api_check.sh clean, it will remove all generated objects that could
|
||||
# be used for further inspection.
|
||||
#
|
||||
# The script works by compiling the pg_stat_monitor extension on both source and
|
||||
# target branches, then it (re)creates the extension on PostgreSQL, dump the
|
||||
# database objects (views, functions) and compare the results.
|
||||
#
|
||||
# All the relevant log is output on console (stdout/1).
|
||||
#
|
||||
# Following exit codes are used:
|
||||
# 0 (SUCCESS) API between branches hasn't changed.
|
||||
# 1 (ERROR) Script failed to execute some step.
|
||||
# 10 (API CHANGED) Target branch has either removed or modified some database
|
||||
# object that was exported by the source branch.
|
||||
#
|
||||
#
|
||||
# REQUIRES: Following environment variables must be properly set.
|
||||
# PATH Must include path to the pg_config relative to the PostgreSQL instance to be used.
|
||||
# PGDATA Must point to the database cluster directory to be used.
|
||||
|
||||
function usage() {
|
||||
echo "[*] Usage: $0 [source_branch/commit_id] target_branch/commit_id"
|
||||
echo "[*] OR"
|
||||
echo "[*] Usage: $0 clean"
|
||||
echo
|
||||
echo "$0 checks for API compatibility between the source_branch and"\
|
||||
"target_branch."
|
||||
echo
|
||||
echo "If invoked with clean argument, the script just removes all generated"\
|
||||
"files that could be used for further inspection."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check number of command line arguments.
|
||||
[ $# -eq 0 ] &&
|
||||
{
|
||||
usage
|
||||
}
|
||||
|
||||
[ "$1" = "-h" -o "$1" = "--help"] &&
|
||||
{
|
||||
usage
|
||||
}
|
||||
|
||||
if [ "$1" = "clean" ]; then
|
||||
rm -vf api_pgsmview.*
|
||||
rm -vf api_pgsmerrview.*
|
||||
rm -vf api_histogramfn.*
|
||||
rm -vf api_resetfn.*
|
||||
rm -vf api_reseterrfn.*
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Get absolute directory path to the script.
|
||||
# https://stackoverflow.com/questions/4774054/reliable-way-for-a-bash-script-to-get-the-full-path-to-itself
|
||||
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
|
||||
# Change working dir to the project directory to be able to run git commands.
|
||||
cd "${SCRIPTPATH}"
|
||||
|
||||
# Save original git branch.
|
||||
ORIG_BRANCH="`git rev-parse --abbrev-ref HEAD`"
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
# Source git branch.
|
||||
SOURCE_BRANCH="$1"
|
||||
# Target branch to compare API with.
|
||||
TARGET_BRANCH="$2"
|
||||
# Check if source branch actually exists.
|
||||
if ! git rev-parse --verify "${SOURCE_BRANCH}"; then
|
||||
echo "[*] ERROR: Source branch do not exist: ${SOURCE_BRANCH}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Current git branch.
|
||||
SOURCE_BRANCH="`git rev-parse --abbrev-ref HEAD`"
|
||||
# Target branch to compare API with.
|
||||
TARGET_BRANCH="$1"
|
||||
fi
|
||||
|
||||
# Check that target branch actually exists.
|
||||
if ! git rev-parse --verify "${TARGET_BRANCH}"; then
|
||||
echo "[*] ERROR: Target branch do not exist: ${TARGET_BRANCH}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for pg_config, POSIX compatible way.
|
||||
if ! command -v pg_config > /dev/null 2>&1; then
|
||||
echo "[*] ERROR: Could not locate pg_config, please adjust PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PG_BINDIR=`pg_config --bindir`
|
||||
PGCTL="${PG_BINDIR}/pg_ctl"
|
||||
PSQL="${PG_BINDIR}/psql"
|
||||
|
||||
[ ! -x "${PGCTL}" ] &&
|
||||
{
|
||||
echo "[*] ERROR: pg_ctl not found/no +x permission: ${PGCTL}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -x "${PSQL}" ] &&
|
||||
{
|
||||
echo "[*] ERROR: psql not found/no +x permission: ${PSQL}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ -z "$PGDATA" ] &&
|
||||
{
|
||||
echo "[*] ERROR: PGDATA environment must be set and point to your\
|
||||
PostgreSQL database directory"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ ! -d "$PGDATA" ] &&
|
||||
{
|
||||
echo "[*] ERROR: PGDATA directory not found: $PGDATA"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Remove temporary files when script exits.
|
||||
function cleanup() {
|
||||
rm -f err
|
||||
if [ -f pgconf.orig ]; then
|
||||
cp -f pgconf.orig "${PGDATA}/postgresql.conf"
|
||||
rm -f pgconf.orig
|
||||
fi
|
||||
rm -f pgsm.conf
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
PG_VER="`cat ${PGDATA}/PG_VERSION 2> /dev/null`"
|
||||
|
||||
# Make a copy of original postgresql.conf.
|
||||
cp -f "${PGDATA}"/postgresql.conf ./pgconf.orig
|
||||
# Remove any shared_preload_libraries configuration.
|
||||
grep -v shared_preload_libraries pgconf.orig > pgsm.conf
|
||||
# Add pg_stat_monitor extension to the config.
|
||||
echo "shared_preload_libraries = 'pg_stat_monitor'" >> pgsm.conf
|
||||
# Overwrite postgresql configuration.
|
||||
cp pgsm.conf "${PGDATA}/postgresql.conf"
|
||||
|
||||
function restart_postgres() {
|
||||
echo "[*] Restarting PostgreSQL ${PG_VER}..."
|
||||
cd "$PGDATA"
|
||||
if ! $PGCTL -D "$PGDATA" restart 2> err > /dev/null; then
|
||||
echo "[*] ERROR: Failed to restart PostgreSQL ${PG_VER}: `cat err`"
|
||||
exit 1
|
||||
fi
|
||||
cd -
|
||||
}
|
||||
|
||||
# Dump information about views, functions, or any object used as API.
|
||||
# 1. Compile and install pg_stat_monitor.
|
||||
# 2. Restart PostgreSQL.
|
||||
# 3. Create the extension.
|
||||
# 4. Dump pg_stat_monitor views, functions, etc...
|
||||
function dump_pgsm_api() {
|
||||
BRANCH="$1"
|
||||
echo "[*] Compiling pg_stat_monitor ($BRANCH)"
|
||||
make USE_PGXS=1 clean
|
||||
if ! make USE_PGXS=1 install; then
|
||||
echo "[*] Compilation failed, aborting..."
|
||||
exit 1
|
||||
fi
|
||||
restart_postgres
|
||||
"$PSQL" -c 'DROP EXTENSION IF EXISTS pg_stat_monitor;' 2> /dev/null
|
||||
"$PSQL" -c 'CREATE EXTENSION pg_stat_monitor;' 2> /dev/null
|
||||
"$PSQL" -c '\d pg_stat_monitor' 2> /dev/null > api_pgsmview."${BRANCH}"
|
||||
"$PSQL" -c '\d pg_stat_monitor_errors' 2> /dev/null > api_pgsmerrview."${BRANCH}"
|
||||
"$PSQL" -c '\sf histogram' 2> /dev/null > api_histogramfn."${BRANCH}"
|
||||
"$PSQL" -c '\sf pg_stat_monitor_reset' 2> /dev/null > api_resetfn."${BRANCH}"
|
||||
"$PSQL" -c '\sf pg_stat_monitor_reset_errors' 2> /dev/null > api_reseterrfn."${BRANCH}"
|
||||
}
|
||||
|
||||
# Checkout source branch.
|
||||
# If source branch was not given on command line, use current
|
||||
# git branch (skip checkout).
|
||||
TMP_SRC_BRANCH=""
|
||||
if [ $# -gt 1 ]; then
|
||||
RANDSTR=`echo $RANDOM | md5sum | head -c 20`
|
||||
TMP_SRC_BRANCH="source_${RANDSTR}_${SOURCE_BRANCH}"
|
||||
echo "[*] Checking out ${SOURCE_BRANCH} on temporary branch ${TMP_SRC_BRANCH}"
|
||||
|
||||
if ! git checkout -b "${TMP_SRC_BRANCH}" "${SOURCE_BRANCH}" 2> /dev/null; then
|
||||
echo "[*] ERROR: Failed to checkout branch ${SOURCE_BRANCH}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] Dumping API (${SOURCE_BRANCH})..."
|
||||
dump_pgsm_api "${SOURCE_BRANCH}"
|
||||
make USE_PGXS=1 clean
|
||||
|
||||
RANDSTR=`echo $RANDOM | md5sum | head -c 20`
|
||||
TMP_DST_BRANCH="target_${RANDSTR}_${TARGET_BRANCH}"1
|
||||
echo "[*] Checking out ${TARGET_BRANCH} on temporary branch ${TMP_DST_BRANCH}"
|
||||
|
||||
if ! git checkout -b "${TMP_DST_BRANCH}" "${TARGET_BRANCH}" 2> /dev/null;
|
||||
then
|
||||
echo "[*] ERROR: Failed to checkout branch ${TARGET_BRANCH}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove temporary source branch (if created).
|
||||
[ -z "${TMP_SRC_BRANCH}"] || git branch -D "${TMP_SRC_BRANCH}"
|
||||
|
||||
echo "[*] Dumping API (${TARGET_BRANCH})..."
|
||||
dump_pgsm_api "${TARGET_BRANCH}"
|
||||
make USE_PGXS=1 clean
|
||||
|
||||
# Restore original branch and remove temporary one.
|
||||
git checkout "${ORIG_BRANCH}"
|
||||
git branch -D "${TMP_DST_BRANCH}"
|
||||
|
||||
echo
|
||||
echo "[*] ------ API comparison results ------"
|
||||
echo "[*]"
|
||||
|
||||
# Input files
|
||||
INPUT=(api_pgsmview api_pgsmerrview api_histogramfn api_resetfn api_reseterrfn)
|
||||
# Real API object names.
|
||||
API_OBJECTS=("pg_stat_monitor" "pg_stat_monitor_errors" "histogram" "pg_stat_monitor_reset" "pg_stat_monitor_reset_errors")
|
||||
|
||||
STATUS=0
|
||||
i=0
|
||||
while [ $i -lt ${#API_OBJECTS[@]} ]; do
|
||||
echo "[*] ${API_OBJECTS[$i]}:"
|
||||
|
||||
# Compute input file sizes.
|
||||
fs0=$(stat -c '%s' ${INPUT[$i]}."${SOURCE_BRANCH}")
|
||||
fs1=$(stat -c '%s' ${INPUT[$i]}."${TARGET_BRANCH}")
|
||||
|
||||
if [ $fs0 -eq 0 ]; then
|
||||
echo -e "\tNot present in branch ${SOURCE_BRANCH}"
|
||||
if [ $fs1 -gt 0 ]; then
|
||||
echo -e "\tObject added on target branch ${TARGET_BRANCH}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $fs1 -eq 0 ]; then
|
||||
if [ $fs0 -gt 0 ]; then
|
||||
# Object was present in original but removed on target branch.
|
||||
echo -e "\tRemoved on branch ${TARGET_BRANCH}"
|
||||
STATUS=10
|
||||
else
|
||||
echo -e "\tNot present in branch ${TARGET_BRANCH}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $fs0 -gt 0 -a $fs1 -gt 0 ]; then
|
||||
if ! diff ${INPUT[$i]}."${SOURCE_BRANCH}" ${INPUT[$i]}."${TARGET_BRANCH}" > ${API_OBJECTS[$i]}.diff; then
|
||||
echo -e "\tAPI is different, diff (${API_OBJECTS[$i]}.diff):"
|
||||
cat ${API_OBJECTS[$i]}.diff
|
||||
STATUS=10
|
||||
else
|
||||
echo -e "\tAPI is compatible (OK)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] -----------------------------"
|
||||
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
|
||||
exit $STATUS
|
Loading…
Reference in New Issue