-
-
Save nccurry/40656d9c1cab05c79e507339b96a562a to your computer and use it in GitHub Desktop.
OCP4: Validate release available and download oc/openshift-install clients
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
#!/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