Skip to content

Instantly share code, notes, and snippets.

@ryran
Created January 24, 2020 04:41
Show Gist options
  • Save ryran/0f41161a7638f13ad18d61af0daec0f8 to your computer and use it in GitHub Desktop.
Save ryran/0f41161a7638f13ad18d61af0daec0f8 to your computer and use it in GitHub Desktop.
OCP4: Validate release available and download oc/openshift-install clients
#!/bin/bash
# ocp4-download-clients v0.2.0 last mod 2020/01/23
# Copyright 2020 Ryan Sawhill Aroha <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License <gnu.org/licenses/gpl.html> for more details.
# ██ ██ █████ ██ ██████
# ██ ██ ██ ██ ██ ██ ██
# ███████ ███████ ██ ██████
# ██ ██ ██ ██ ██ ██
# ██ ██ ██ ██ ███████ ██
version=$1
if [[ $version =~ ^4\.[0-9]+\.[0-9]+$ ]]; then
major=${1%.*}
minor=${1##*.}
else
cat <<-EOF
usage: ${0##*/} OCP_VERSION
Download openshift-install & openshift-client tarballs for OCP_VERSION
OCP_VERSION should be something like "4.1.15" or "4.2.2"
Tasks:
• Figures out the highest upgrade channel in which OCP_VERSION can be found
Ordering: "stable", "fast", "candidate", "prerelease"
• Checks whether the release-provided errata advisory URL has been published
• Checks whether the Quay release image is available
• Uses RH PGP key to validate sha256sum.txt.sig
• Downloads openshift-{install,client} tarballs if not already in CWD
• Validates file checksums against those in sha256sum.txt.sig
• Retries downloads in a loop (up to a point) if any of that fails
Requires:
• curl
• gpg
• sed, gawk
• yq (https://github.com/mikefarah/yq/releases)
• jq (https://github.com/stedolan/jq/releases)
Set NO_COLORS=1 to disable ANSI escape code colors
EOF
exit 1
fi
# ███████ ███████ ████████ ██ ██ ██████
# ██ ██ ██ ██ ██ ██ ██
# ███████ █████ ██ ██ ██ ██████
# ██ ██ ██ ██ ██ ██
# ███████ ███████ ██ ██████ ██
# Resources
url=https://mirror.openshift.com/pub/openshift-v4/clients/ocp/$version
release=$version-release.txt
client=openshift-client-linux-$version.tar.gz
install=openshift-install-linux-$version.tar.gz
digest=$version-sha256sum.txt.sig
# They changed things up starting with 4.1.30 and 4.2.14
if [[ $major == 4.1 && $minor -lt 30 ]] || [[ $major == 4.2 && $minor -lt 14 ]]; then
releaseTag=$version
else
releaseTag=$version-x86_64
fi
releaseImage=https://quay.io/v2/openshift-release-dev/ocp-release/manifests/$releaseTag
# Colors
[[ ${NO_COLORS} ]] || declare -A c=([z]='\033[0;0m' [Q]='\033[0;0m\033[1;1m' [r]='\033[0;31m' [R]='\033[1;31m' [g]='\033[0;32m' [G]='\033[1;32m' [y]='\033[0;33m' [Y]='\033[1;33m' [b]='\033[0;34m' [B]='\033[1;34m' [m]='\033[0;35m' [M]='\033[1;35m' [c]='\033[0;36m' [C]='\033[1;36m')
print() {
local echo_opts=
while [[ $1 =~ ^- ]]; do
echo_opts+="$1 "
shift
done
echo -e $echo_opts "$@"
}
validate_url() {
local errs
errs=$(curl --fail -o /dev/null -LSsI $1 2>&1) && return
print "$indent${c[R]}✘ Failed to get $1${c[z]}${c[r]}\n$indent $errs${c[z]}"
return 2
}
download_file() {
local remote=$1 dest=$2 toDest= try=1
[[ $dest ]] && toDest=" to ./$dest" || dest=$1
print "\n\t${c[Y]}Downloading $url/$remote$toDest ...${c[z]}"
print "${c[b]}" >&2
until (( try == 3 )); do
if curl --fail -Lo "$dest" "$url/$remote"; then
print -n "${c[z]}" >&2
return
else
print "\n\t${c[r]} ✘ Failure trying to get this file (attempt #$try)${c[z]}"
print "${c[b]}" >&2
fi
((try++))
done
print "\n\t${c[R]}✘ Giving up on getting this file${c[z]}"
exit 2
}
# ██████ ██████ ███████ ██████ ███████ ██████ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ ██████ █████ █████ ██████ █████ ██ ██ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ▄▄ ██ ██
# ██ ██ ██ ███████ ██ ██ ███████ ██████ ███████
# ▀▀
for cmd in curl sed gawk; do
if ! command -v $cmd >/dev/null; then
print "${c[R]}✘ Missing '$cmd' command!${c[z]}"
exit 1
fi
done
yq_help=$(yq -h)
if [[ $? -gt 0 || ! $yq_help =~ "yq is a lightweight and portable command-line YAML processor" ]]; then
print "${c[R]}✘ Unexpected error running 'yq --help' command -- need yq from https://github.com/mikefarah/yq/releases${c[z]}"
exit 1
fi
jq_help=$(jq -h)
if [[ $? -gt 0 || ! $jq_help =~ "jq - commandline JSON processor" ]]; then
print "${c[R]}✘ Unexpected error running 'jq --help' command -- need jq from https://github.com/stedolan/jq/releases${c[z]}"
exit 1
fi
if command -v gpg >/dev/null; then
gpg=gpg
elif command -v gpg2 >/dev/null; then
gpg=gpg2
else
print "${c[R]}✘ Missing 'gpg'/'gpg2' command! ... !?!?${c[z]}"
exit 1
fi
gpg_vers=$($gpg --version)
if [[ $? -gt 0 || $gpg_version =~ GnuPG ]]; then
print "${c[R]}✘ Unexpected error running '$gpg --version' command ... !?!?${c[z]}"
exit 1
fi
# ██████ ██████ ██████ ██ ██ ███████ ██ ██ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██ ███ ██████ ██ ███ █████ █████ ████ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ ██ ██████ ██ ██ ███████ ██ ███████
# Ensure we have release key for verification
keyid=FD431D51
if ! $gpg -k $keyid &>/dev/null; then
# Try to import it from RHEL location
key=/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
[[ -r $key ]] && $gpg --import $key
fi
# If still don't have it, try to download it
if ! $gpg -k $keyid &>/dev/null; then
key=${keyid,,}.txt
if ! curl -LSsO https://www.redhat.com/security/data/$key; then
print "${c[R]}✘ Failed to get Red Hat $keyid release key (see https://access.redhat.com/security/team/key)${c[z]}"
exit 1
fi
$gpg --import $key
fi
# If still don't have it, abort
if ! $gpg -k $keyid &>/dev/null; then
print "${c[R]}✘ Problem with gpg -- still can't see $keyid key in public keyring${c[z]}"
exit 1
fi
# ██████ ██ ██ ██ ██ ██████ ███████ ██ ███████ █████ ███████ ███████
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██ ███████ █████ ██████ █████ ██ █████ ███████ ███████ █████
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ ██ ██ ██ ██ ██ ██ ███████ ███████ ███████ ██ ██ ███████ ███████
# Only do this stuff if we don't have ./$version-release.txt
if ! [[ -s $release ]]; then
# Make sure release exists
validate_url $url/release.txt || exit
# Grab release.txt and remove the non-yaml stuff at the beginning
curl -LSs $url/release.txt | sed -n '/^---$/,$p' >$release
fi
# If yq read fails, we have a problem
if ! images=$(yq read $release Images); then
print "${c[R]}✘ Unexpected error running 'yq read' -- need yq from https://github.com/mikefarah/yq/releases${c[z]}"
exit 1
fi
# If there is no "Images" attr, we have a problem
if [[ $images == null ]]; then
print "${c[R]}✘ Couldn't find yaml 'Images' attr in $url/$release${c[z]}"
exit 3
fi
# Remove 'Images' attr
releaseInfo=$(yq delete $release Images)
# Fix bad yaml -- duplicate Metadata attr
if [[ $(yq read - 'Release Metadata' <<<"${releaseInfo}" | grep ^Metadata: | wc -l) == 2 ]]; then
releaseInfo=$(gawk '!/^ Metadata:$/ || ++ctr != 2' <<<"${releaseInfo}")
fi
# Get errata
errataUrl=$(yq read - 'Release Metadata.Metadata.url' <<<"${releaseInfo}")
# Check upgrade channel
for channel in stable fast candidate prerelease x; do
out=$(curl -sSH "Accept: application/json" "https://api.openshift.com/api/upgrades_info/v1/graph?channel=${channel}-${major}&arch=amd64" | jq -er --arg version $version '.nodes[] | select(.version == $version) | .version')
[[ $? == 0 && $out == $version ]] && break
done
case $channel in
x)
channelMsg="${c[R]} ☢️ ☣️ U N K N O W N ☣️ ☢️ \n${c[r]} WARNING: Could not find release in ANY upgrade channel!" ;;
stable)
channelMsg="${c[G]}✔✔✔ $channel ✔✔✔" ;;
fast)
channelMsg="${c[Y]}⚠️ ⚠️ $channel ⚠️ ⚠️ \n${c[m]} WARNING: Release has not (yet) been tagged into the stable channel" ;;
candidate|prerelease)
channelMsg="${c[R]}⛔ ⛔ $channel ⛔ ⛔ \n${c[m]} WARNING: Release has not (yet) been tagged into the stable channel" ;;
esac
# Print release details
print "\n${c[Q]}Red Hat OpenShift Container Platform v$major release $minor${c[z]}"
print "${c[Q]}--------------------------------------------------------------------------------${c[z]}"
print "\nRelease source: $url${c[z]}"
print "\nRelease upgrade channel: $channelMsg${c[z]}"
print "\n${c[b]} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${c[z]}" >&2
print "${c[b]}release.txt info:" >&2
sed 's/^/ /' <<<"$releaseInfo" >&2
print "${c[b]} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${c[z]}" >&2
# Make sure errata advisory exists
print "\n${c[Q]}• Release-provided errata URL${c[z]}"
print "\n\t${c[Y]}Fetching ...${c[z]}"
if indent='\t ' validate_url $errataUrl; then
print "\t ${c[G]}✔ GOOD - Errata advisory ($errataUrl) has been published!${c[z]}"
else
print "\n\t ${c[m]}WARNING: The fact that the errata advisory URL provided by the\n\t release.txt is unreachable could signal that this version of\n\t OCP is very new${c[z]}"
fi
# Make sure release manifest exists
print "\n${c[Q]}• Quay release image URL${c[z]}"
print "\n\t${c[Y]}Fetching ...${c[z]}"
if indent='\t ' validate_url $releaseImage; then
print "\t ${c[G]}✔ GOOD - Quay release image ($releaseImage) is available!${c[z]}"
else
print "\n\t ${c[m]}WARNING: The fact that the Quay release image is unavailable\n\t will likely prevent you from performing a successful install${c[z]}"
fi
# ███████ ██ ██ █████ ██████ ███████ ██████ ███████ ██ ██ ███ ███
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████
# ███████ ███████ ███████ █████ ███████ ███████ ███████ ██ ██ ██ ████ ██
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ███████ ██ ██ ██ ██ ███████ ███████ ██████ ███████ ██████ ██ ██
# Download shasum file
print "\n${c[Q]}• $digest${c[z]}"
attempt=0 maxtries=4
while :; do
((attempt++))
if ((attempt > maxtries)); then
print "\n\t${c[R]}✘ Giving up on getting this file after $maxtries attempts${c[z]}"
exit 3
fi
if [[ -s $digest ]]; then
print "\n\t${c[g]}Already downloaded${c[z]}"
else
download_file sha256sum.txt.sig $digest
fi
print "\n\t${c[Y]}Validating ...${c[z]}"
# Verify shasum file with gpg
verify=$($gpg --trust-model always --verify $digest 2>&1)
if (($? == 0)); then
print "\t ${c[G]}✔ GOOD - embedded signature verified!${c[z]}"
print "\t ${c[g]}$(grep 'Good signature from' <<<"$verify")${c[z]}"
# We're done
break
else
print "\t ${c[R]}✘ Unable to verify integrity of digest file $digest${c[z]}"
print "${c[r]}$(sed 's/^/\t /' <<<"$verify")${c[z]}"
# Let's try again
if (( attempt < maxtries )); then
print "\n\t${c[M]}(Attempting to re-download)${c[z]}"
# Let's try again
rm -f $digest
fi
fi
done
# Let's parse it one more time to extract the shasums
shasums=$($gpg --trust-model always -d $digest 2>/dev/null)
# ██████ ██████ ██ ██ ███ ██ ██ ██████ █████ ██████
# ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██
# ██ ██ ██ ██ ██ █ ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██
# ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
# ██████ ██████ ███ ███ ██ ████ ███████ ██████ ██ ██ ██████
rc=0
for f in $client $install; do
print "\n${c[Q]}• $f${c[z]}"
attempt=0 maxtries=4
while :; do
((attempt++))
if ((attempt > maxtries)); then
print "\n\t${c[R]}✘ Giving up on getting this file after $maxtries attempts${c[z]}"
rc=3
# Move on to next file
break
fi
if [[ -s $f ]]; then
print "\n\t${c[g]}Already downloaded${c[z]}"
else
download_file $f
fi
print "\n\t${c[Y]}Checksumming ...${c[z]}"
if ! expected=$(grep $f <<<"$shasums"); then
print "\t ${c[R]}✘ Digest file $digest didn't contain file '$f'${c[z]}"
rc=3
# Move on to next file
break
fi
if ! actual=$(sha256sum $f 2>&1); then
print "\t ${c[R]}✘ Error running sha256sum against file '$f'\n\t $actual${c[z]}"
if (( attempt < maxtries )); then
print "\n\t${c[M]}(Attempting to re-download)${c[z]}"
# Let's try again
rm -f $f
fi
continue
fi
if [[ $expected == $actual ]]; then
print "\t ${c[G]}✔ GOOD - checksums match!${c[z]}"
break
else
print "\t ${c[R]}✘ CHECKSUM MISMATCH! LIKELY CORRUPTED FILE!${c[z]}"
print "\t ${c[g]}$expected${c[z]} (expected)"
print "\t ${c[r]}$actual${c[z]} (actual)"
if (( attempt < maxtries )); then
print "\n\t${c[M]}(Attempting to re-download)${c[z]}"
# Let's try again
rm -f $f
fi
continue
fi
done
done
exit $rc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment