Skip to content

Instantly share code, notes, and snippets.

@nicolas-goudry
Last active January 15, 2024 14:20
Show Gist options
  • Save nicolas-goudry/7385f00bb50cc46cbf0763110bc0ffa0 to your computer and use it in GitHub Desktop.
Save nicolas-goudry/7385f00bb50cc46cbf0763110bc0ffa0 to your computer and use it in GitHub Desktop.
Bash script to retrieve kcadm.sh from Keycloak server running on Kubernetes
#!/usr/bin/env bash
# Exit on error
set -e
# Variables
SCRIPT_NAME="$(basename "$0")"
OUTDIR="$PWD/kcadm"
# Color codes
NC="\e[0m"
BOLD="\e[1m"
DIM="\e[2m"
Y="\e[33m"
R="\e[31m"
G="\e[32m"
# Utility function to output error message
error() {
>&2 echo -e "${R}ERROR: $*${NC}"
}
# Utility function to output warning message
warn() {
>&2 echo -e "${Y}WARN: $*${NC}"
}
# Utility function to exit script with optional error message
die() {
if test "$#" -gt 0; then
error "$*"
fi
exit 1
}
# Output help usage
usage() {
echo
echo "Retrieve necessary files to get a working local copy of the Keycloak"
echo "admin CLI (ie. kcadm.sh)."
echo
echo -e "${BOLD}Usage:${NC}"
echo
echo -e " ${DIM}\$${NC} $SCRIPT_NAME [OPTIONS]"
echo
echo -e "${BOLD}Options:${NC}"
echo
echo " -v, --version Set Keycloak server version"
echo " -h, --help Show this help message"
echo
}
_kubectl() {
kubectl -n "$NAMESPACE" "$@"
}
# Utility function to get an item list returned by a provided command
# and ask user to choose one item
list_select() {
local question="$1"
local destination="${2:-SELECTED_ITEM}"
local data_cmd="$3"
local ifs="$4"
if test -z "$question"; then
die "no question specified"
fi
if test -z "$data_cmd"; then
die "no command to get data was provided"
fi
local data=()
while IFS=$ifs read -r line; do
data+=("$line")
done < <($data_cmd)
local len=${#data[@]}
if test "$len" -eq 0; then
die "no data found"
elif test "$len" -eq 1; then
export "$destination"="${data[0]}"
else
local choice=""
while test -z "$choice"; do
echo "$question"
for i in "${!data[@]}"; do
echo -e "\t[$i] ${data[i]}"
done
read -rep "Select an item: " "choice"
if test -z "${data[choice]}"; then
error "invalid choice"
choice=""
fi
done
export "$destination"="${data[choice]}"
fi
}
# Check that kubectl command is available
check_kubectl() {
if test -z "$(command -v kubectl)"; then
die "missing kubectl binary"
fi
}
# Check output directory does not already contain kcadm.sh
check_outdir() {
if test -f "$OUTDIR/kcadm.sh"; then
warn "kcadm.sh is already installed."
read -rep "Continue anyway? [Y/n] " "choice"
case $choice in
[nN] ) exit;;
esac
fi
}
# Get version used in container by looking at the container image tag
get_version() {
if test -n "$VERSION"; then
echo "${DIM}Assuming Keycloak version $VERSION as requested by user.${NC}"
else
local version
version=$(_kubectl get po "$POD" -ojsonpath='{.spec.containers[0].image}' | grep -oP '.+:\K(\d+.\d+.\d+)$')
if test -z "$version"; then
error "failed to detect keycloak version"
echo "Use ${DIM}--version${NC} option to manually set the Keycloak server version"
die
fi
VERSION="$version"
fi
}
get_files() {
mkdir -p "$OUTDIR"/client/lib
_kubectl exec -i -c keycloak "$POD" -- cat /opt/keycloak/bin/kcadm.sh > "$OUTDIR"/kcadm.sh
chmod +x "$OUTDIR"/kcadm.sh
_kubectl exec -i -c keycloak "$POD" -- cat /opt/keycloak/bin/client/keycloak-admin-cli-"$VERSION".jar > "$OUTDIR"/client/keycloak-admin-cli-"$VERSION".jar
local libfiles
libfiles=$(_kubectl exec -i -c keycloak "$POD" -- ls /opt/keycloak/bin/client/lib)
for libfile in $libfiles; do
_kubectl exec -i -c keycloak "$POD" -- cat /opt/keycloak/bin/client/lib/"$libfile" > "$OUTDIR"/client/lib/"$libfile"
done
}
# Read script flags
while getopts 'hv:-:' OPT; do
# support long options: https://stackoverflow.com/a/28466267/519360
if test "$OPT" = "-"; then # long option: reformulate OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
# shellcheck disable=SC2295
OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty)
OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=`
fi
# Handle flags
case "$OPT" in
v | version ) VERSION="$OPTARG" ;;
h | help ) usage; exit 0 ;;
??* ) die "illegal option --${OPT}" ;; # bad long option
? ) exit 1 ;; # bad short option (error reported via getopts)
esac
done
main() {
echo -e "${DIM}Performing initial checks...${NC}"
check_kubectl
check_outdir
list_select "Select Keycloak Namespace:" "NAMESPACE" "kubectl get ns -ocustom-columns=NAME:.metadata.name --no-headers"
list_select "Select Pod:" "POD" "kubectl get pods -n $NAMESPACE --field-selector=status.phase=Running -ocustom-columns=NAME:.metadata.name --no-headers"
echo -e "${DIM}Detecting Keycloak version...${NC}"
get_version
echo -e "${DIM}Retrieving files...${NC}"
get_files
echo -e "${G}Keycloak admin CLI is available in $OUTDIR.${NC}"
echo -e "${DIM}Run \"PATH=$OUTDIR:\$PATH\" to make it available system wide.${NC}"
}
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment