-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EMP-2850 Introducing the Image Helper script
Signed-off-by: Tapas Sharma <[email protected]>
- Loading branch information
1 parent
05c9d42
commit fe10c52
Showing
1 changed file
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,320 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Detect shell and set appropriate options | ||
if [ -n "$ZSH_VERSION" ]; then | ||
# Zsh-specific settings | ||
setopt SH_WORD_SPLIT # Ensure word splitting behavior similar to sh/bash | ||
setopt NULLGLOB # Allow empty globs | ||
setopt POSIX_BUILTINS # Use POSIX standard for builtins | ||
elif [ -n "$BASH_VERSION" ]; then | ||
# Bash-specific settings | ||
set -o pipefail | ||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' | ||
fi | ||
|
||
# Strict error handling | ||
set -eu | ||
|
||
IMAGES="" | ||
IMAGES="$IMAGES registry.k8s.io/pause:3.1" | ||
IMAGES="$IMAGES registry.k8s.io/kube-controller-manager-amd64:v1.30.2" | ||
IMAGES="$IMAGES registry.k8s.io/kube-scheduler-amd64:v1.30.2" | ||
IMAGES="$IMAGES docker.io/nginxinc/nginx-unprivileged:1.25" | ||
IMAGES="$IMAGES registry.k8s.io/kube-scheduler-amd64:v1.21.4" | ||
IMAGES="$IMAGES registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.9.0" | ||
IMAGES="$IMAGES registry.k8s.io/sig-storage/csi-provisioner:v3.6.1" | ||
IMAGES="$IMAGES registry.k8s.io/sig-storage/csi-resizer:v1.9.1" | ||
IMAGES="$IMAGES registry.k8s.io/sig-storage/csi-snapshotter:v8.0.1" | ||
IMAGES="$IMAGES registry.k8s.io/sig-storage/snapshot-controller:v6.3.1" | ||
IMAGES="$IMAGES quay.io/prometheus/prometheus:v2.48.1" | ||
IMAGES="$IMAGES quay.io/prometheus-operator/prometheus-operator:v0.70.0" | ||
IMAGES="$IMAGES quay.io/prometheus-operator/prometheus-config-reloader:v0.70.0" | ||
IMAGES="$IMAGES quay.io/prometheus/alertmanager:v0.26.0" | ||
|
||
VERBOSE=0 | ||
K8S_CMD=kubectl | ||
K8SSECRET=emp-secret | ||
DRYRUN=0 | ||
CNT_CMD=docker | ||
|
||
# Colors init via tput | ||
Cg="$(tput bold 2>/dev/null && tput setaf 0 2>/dev/null || /bin/true)" # Gray | ||
Cb="$(tput bold 2>/dev/null && tput setaf 7 2>/dev/null && tput setab 4 2>/dev/null || /bin/true)" # White on Blue | ||
Cy="$(tput bold 2>/dev/null && tput setaf 3 2>/dev/null && tput setab 0 2>/dev/null || /bin/true)" # Yellow on Black | ||
Cr="$(tput bold 2>/dev/null && tput setaf 3 2>/dev/null && tput setab 1 2>/dev/null || /bin/true)" # Yellow on Red | ||
CR="$(tput sgr0 2>/dev/null || /bin/true)" # RESET | ||
|
||
# Spinner animation | ||
spinner() { | ||
local pid=$1 | ||
local delay=0.1 | ||
local spinstr='|/-\' | ||
local temp="" | ||
|
||
while kill -0 "$pid" 2>/dev/null; do | ||
temp="${spinstr#?}" | ||
spinstr=$temp${spinstr%"$temp"} | ||
printf "\r${Cb} ⋯ %s ${CR}" "${spinstr:0:1}" >&2 | ||
sleep "$delay" | ||
done | ||
printf "\r${Cg}✓ Complete${CR}\n" >&2 | ||
} | ||
|
||
# debug - prints a DEBUG message (normally gray on black) | ||
debug() { | ||
[ $VERBOSE -le 0 ] || echo "$(date +'%F %T') $Cg DEBUG: $* $CR" >&2 | ||
} | ||
|
||
# info - prints an INFO message (normally white on blue) | ||
info() { | ||
echo "$(date +'%F %T') $Cb INFO: $* $CR" >&2 | ||
} | ||
|
||
# warn - prints a WARN message (normally yellow on black) | ||
warn() { | ||
echo "$(date +'%F %T') $Cy WARN: $* $CR" >&2 | ||
} | ||
|
||
# fail - prints a FATAL message (yellow on red) and exits the script | ||
fail() { | ||
local args=${*:-error} | ||
echo "$(date +'%F %T') $Cr FATAL $args $CR" >&2 | ||
trap - EXIT | ||
exit 2 | ||
} | ||
|
||
# SUPPORTING FUNCTIONS | ||
|
||
exit_trap() { | ||
local rc=$? | ||
if [ $rc -eq 0 ]; then | ||
debug "END" | ||
else | ||
warn "$0 FAILED (rc=$rc)" | ||
fi | ||
exit $rc | ||
} | ||
trap exit_trap EXIT | ||
|
||
# dummy_cmd - replacement for docker/kubelet to display parameters/output | ||
dummy_cmd() { | ||
if echo "$*" | grep -q -- '-$'; then | ||
echo "DRY-RUN: $* << EOF" >&2 | ||
cat >&2 | ||
echo EOF >&2 | ||
else | ||
echo "DRY-RUN: $*" 2>&1 | ||
fi | ||
} | ||
|
||
# num_images returns a number of images | ||
num_images() { | ||
# shellcheck disable=SC2086 | ||
echo $IMAGES | wc -w | ||
} | ||
|
||
# pull_images - pulls container images with spinner | ||
pull_images() { | ||
local num_img | ||
num_img=$(num_images) | ||
info "Pulling $num_img images for Elastic Machine Pool by Platform9" | ||
|
||
# Temporary file for output | ||
local tmpfile | ||
tmpfile=$(mktemp) | ||
|
||
# Async pull with spinner | ||
{ | ||
for img in $IMAGES; do | ||
info "Pulling $img" | ||
$CNT_CMD pull "$img" 2>&1 | ||
done | ||
} > "$tmpfile" 2>&1 & | ||
|
||
# Capture PID of background process | ||
local pull_pid=$! | ||
|
||
# Show spinner while pulling | ||
spinner "$pull_pid" | ||
|
||
# Check pull results | ||
if wait "$pull_pid"; then | ||
info "Successfully pulled $num_img images" | ||
else | ||
warn "Some images failed to pull. Check the log:" | ||
cat "$tmpfile" >&2 | ||
fi | ||
|
||
# Clean up temp file | ||
rm "$tmpfile" | ||
} | ||
|
||
# push_images_registry - loads container images into given registry | ||
push_images_registry() { | ||
[ $# -eq 1 ] || fail "push: Please provide registry server" | ||
local reg=${1%%/} | ||
|
||
local num_img | ||
num_img=$(num_images) | ||
info "Pushing $num_img images into $reg container registry/repository..." | ||
|
||
# Temporary file for output | ||
local tmpfile | ||
tmpfile=$(mktemp) | ||
|
||
# Async push with spinner | ||
{ | ||
# Transformation function for image names | ||
local trans | ||
if echo "$reg" | grep -q /; then | ||
trans() { | ||
echo "$reg/$(basename "$1")" | ||
} | ||
else | ||
trans() { | ||
local i2="${1##docker.io/}" #remove docker.io | ||
local i3="${i2##registry.k8s.io/}" #remove registry.k8s.io from i2 | ||
echo "$reg/${i3##quay.io/}" #remove quay.io from i3 | ||
} | ||
fi | ||
|
||
# Push images | ||
for img in $IMAGES; do | ||
local tg | ||
tg=$(trans "$img") | ||
$CNT_CMD tag "$img" "$tg" | ||
$CNT_CMD push "$tg" | ||
$CNT_CMD rmi "$tg" | ||
debug "Pushed $tg into $reg" | ||
done | ||
} > "$tmpfile" 2>&1 & | ||
|
||
# Capture PID of background process | ||
local push_pid=$! | ||
|
||
# Show spinner while pushing | ||
spinner "$push_pid" | ||
|
||
# Check push results | ||
if wait "$push_pid"; then | ||
info "Finished pushing $num_img images into $reg" | ||
else | ||
warn "Some images failed to push. Check the log:" | ||
cat "$tmpfile" >&2 | ||
fi | ||
|
||
# Clean up temp file | ||
rm "$tmpfile" | ||
} | ||
|
||
# import_secrets creates the YAML that can be applied | ||
# on k8s using the current docker/podman configuration | ||
import_secrets() { | ||
local f | ||
if echo "$CNT_CMD" | grep -qw podman; then | ||
f=/run/user/0/containers/auth.json | ||
[ -f "$f" ] || f=/run/containers/0/auth.json | ||
else | ||
f=$HOME/.docker/config.json | ||
fi | ||
[ -f "$f" ] || fail "Registry secrets file $f not available" | ||
|
||
# Use base64 with appropriate flag for both GNU and BSD base64 | ||
local B64CONTENT | ||
B64CONTENT=$(base64 -w0 "$f" 2>/dev/null || base64 "$f") | ||
|
||
$K8S_CMD apply -f - << EOF | ||
apiVersion: v1 | ||
data: | ||
.dockerconfigjson: $B64CONTENT | ||
kind: Secret | ||
metadata: | ||
name: $K8SSECRET | ||
namespace: kube-system | ||
type: kubernetes.io/dockerconfigjson | ||
EOF | ||
info "Registry secrets kube-system/$K8SSECRET imported from $f" | ||
} | ||
|
||
# usage - prints usage | ||
usage() { | ||
local rc=${1:-0} | ||
local SELF | ||
SELF=$(readlink -f "$0") | ||
[ ! -f "$SELF" ] && SELF='curl -fsSL https://raw.githubusercontent.com/platform9/support-locker/refs/heads/master/emp/custom-registry/emp_image_helper.sh | sh -s --' | ||
cat << _EOF >&2 | ||
Usage: $SELF <options> <commands> | ||
IMAGE-COMMANDS: | ||
pull pulls the Elastic Machine Pool by Platform9 container images locally | ||
push <registry[/repo]> pushes the Elastic Machine Pool by Platform9 images into remote container registry server | ||
load node1 [node2 [...]] loads the images tarball to remote nodes (note: ssh-access required) | ||
OPTIONS: | ||
-n|--dry-run show commands instead of running | ||
-V|--version print version of the script | ||
-v verbose output | ||
EXAMPLES: | ||
# Pull images from default container registries, push them to custom registry server (default repositories) | ||
$SELF pull push your-registry.company.com:5000 | ||
# Pull images from default container registries, push them to custom registry server and Elastic Machine Pool by Platform9 repository | ||
$SELF pull | ||
$SELF push your-registry.company.com:5000/emp-pf9 | ||
# Push images to password-protected remote registry, then import docker/podman configuration as kubernetes secret | ||
$CNT_CMD login your-registry.company.com:5000 | ||
$SELF pull | ||
$SELF push your-registry.company.com:5000/emp-pf9 | ||
$SELF import-secrets | ||
_EOF | ||
exit "$rc" | ||
} | ||
|
||
# Argument parsing with zsh and bash compatibility | ||
[ $# -gt 0 ] || usage | ||
|
||
while [ $# -gt 0 ]; do | ||
case "$1" in | ||
pull) | ||
info "Using '$CNT_CMD' to handle container images" | ||
pull_images | ||
;; | ||
push) | ||
info "Using '$CNT_CMD' to handle container images" | ||
push_images_registry "$2" | ||
shift ;; | ||
import-secrets) | ||
import_secrets | ||
;; | ||
-n|--dry-run) | ||
# dry-run mode | ||
CNT_CMD="dummy_cmd $CNT_CMD" | ||
K8S_CMD="dummy_cmd $K8S_CMD" | ||
LOAD='cat > /dev/null' | ||
DRYRUN=1 | ||
;; | ||
-v) | ||
VERBOSE=$((VERBOSE+1)) | ||
;; | ||
-V|--version) | ||
echo "Elastic Machine Pool by Platform9 v1.2" | ||
shift "$#" | ||
break ;; | ||
-h|--help) | ||
usage | ||
break ;; | ||
--) | ||
shift | ||
break ;; | ||
*) | ||
usage | ||
break ;; | ||
esac | ||
shift | ||
done | ||
|
||
[ $# -eq 0 ] || fail "Unknown argument(s): $*" |