Created
May 18, 2025 16:57
-
-
Save DinoChiesa/edcea80136139dc6d4a9aa0724fd3447 to your computer and use it in GitHub Desktop.
Deploy Apigee bundle with sha-256 checksum in the Description
This file contains hidden or 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 | |
# -*- mode:shell-script; coding:utf-8; -*- | |
# | |
# Copyright © 2025 Google LLC. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# https://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# | |
# | |
# Deploy a bundle for an APIProxy or SharedFlowBundle, from an exploded directory, | |
# updating the description in the bundle file to include a SHA256 sum of | |
# the directory. This will represent a hash of the bundle configuration, and | |
# will serve as a uniquifier to allow comparison of API proxy revisions | |
# across environments. | |
# | |
# Usage: | |
# ENV=eval ORG=my-gcp-apigee-project deploy-with-shasum.sh ./my-bundle-dir/apiproxy | |
# | |
# This utility relies on the following commands: | |
# cp rm mktemp tar shasum xsltproc apigeecli | |
scriptid="deploy-with-shasum" | |
need_wait=0 | |
# Array of environment variable names to check | |
env_vars_to_check=( | |
"ENV" | |
"ORG" | |
) | |
# ==================================================================== | |
shasum_for_dir() { | |
local dir_of_interest SHASUM_FILE value | |
dir_of_interest=$1 | |
if ! [[ -d "$dir_of_interest" ]]; then | |
printf "Directory does not exist. Cannot continue.\n" | |
exit 1 | |
fi | |
SHASUM_FILE=$(mktemp /tmp/${scriptid}.out.XXXXXX) | |
# https://stackoverflow.com/a/5431932 | |
tar -cf - --exclude='*.*~' --exclude='*~' $dir_of_interest | shasum -a 256 >"$SHASUM_FILE" | |
value=$(<"$SHASUM_FILE") | |
value="${value//[$'\t\r\n '-]/}" | |
rm "$SHASUM_FILE" | |
printf "%s" "$value" | |
} | |
update_description_in_bundle() { | |
local bundle_manifest_file shasum asset_type description_value element_name tmp_file | |
bundle_manifest_file="$1" | |
shasum="$2" | |
asset_type="$3" | |
description_value="shasum: $shasum" | |
# The source document looks like this: | |
# | |
# <APIProxy revision="1" name="quota-for-apikey-or-token"> | |
# <ConfigurationVersion minorVersion="0" majorVersion="4"/> | |
# <CreatedBy>dino</CreatedBy> | |
# <Description/> | |
# <LastModifiedBy>orgAdmin</LastModifiedBy> | |
# <TargetEndpoints/> | |
# </APIProxy> | |
# | |
# OR | |
# | |
# <SharedFlowBundle revision="1" name="apigeesample-unified-auth"> | |
# <ConfigurationVersion minorVersion="0" majorVersion="4"/> | |
# <CreatedAt>1433453156684</CreatedAt> | |
# <CreatedBy>[email protected]</CreatedBy> | |
# <Description/> | |
# <DisplayName>apigeesample-unified-auth</DisplayName> | |
# ... | |
# </SharedFlowBundle> | |
if [[ "${asset_type}" = "sharedflowbundle" ]]; then | |
element_name="SharedFlowBundle" | |
elif [[ "${asset_type}" = "apiproxy" ]]; then | |
element_name="APIProxy" | |
else | |
# should never happen | |
printf "unhandled asset type\n" | |
exit 1 | |
fi | |
tmp_file=$(mktemp /tmp/${scriptid}.out.XXXXXX) | |
xsltproc --stringparam newDescriptionValue "" - "$bundle_manifest_file" >"${tmp_file}" <<EOF_XSLT | |
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
<xsl:output method="xml" indent="yes"/> | |
<xsl:param name="newDescriptionValue"/> | |
<xsl:template match="/${element_name}/Description"> | |
<Description>${description_value}</Description> | |
</xsl:template> | |
<xsl:template match="@*|node()"> | |
<xsl:copy> | |
<xsl:apply-templates select="@*|node()"/> | |
</xsl:copy> | |
</xsl:template> | |
</xsl:stylesheet> | |
EOF_XSLT | |
if [[ $? -eq 0 ]]; then | |
mv "${tmp_file}" "$bundle_manifest_file" | |
#printf "Successfully updated the Description element in '%s'\n" "$bundle_manifest_file" | |
true | |
else | |
echo "Error: xsltproc failed to update the XML file." | |
rm -f "${tmp_file}.tmp" # Clean up temporary file | |
false | |
fi | |
} | |
import_and_deploy_with_shasum() { | |
local dirpath asset_type object files bundle_manifest_file name TMP_PROXY_DIR shasum_value | |
dirpath=$1 | |
asset_type=$(basename $dirpath) | |
if [[ "${asset_type}" = "sharedflowbundle" ]]; then | |
object="sharedflows" | |
elif [[ "${asset_type}" = "apiproxy" ]]; then | |
object="apis" | |
else | |
printf "Specify a directory ending in 'sharedflowbundle' or 'apiproxy'\n" | |
exit 1 | |
fi | |
files=(${dirpath}/*.xml) | |
if [[ ${#files[@]} -eq 1 ]]; then | |
bundle_manifest_file="${files[0]}" | |
name=$(basename "${bundle_manifest_file%.*}") | |
# 1. copy the directory to a temp location | |
TMP_PROXY_DIR=$(mktemp -d /tmp/${scriptid}.out.XXXXXX) | |
cp -r "$dirpath" "$TMP_PROXY_DIR" | |
# 2. Compute the checksum for the proxy configuration | |
shasum_value=$(shasum_for_dir "$dirpath") | |
# 3. Update the bundle description to contain the checksum | |
files=(${TMP_PROXY_DIR}/${asset_type}/*.xml) | |
bundle_manifest_file="${files[0]}" | |
if update_description_in_bundle "$bundle_manifest_file" "$shasum_value" "$asset_type"; then | |
# 4. actually import the modified thing | |
printf "Importing a new revision of %s [%s]...\n" "$asset_type" "$name" | |
apigeecli "$object" create bundle -f "${TMP_PROXY_DIR}/${asset_type}" --name "${name}" -o "$ORG" --token "$TOKEN" | |
printf "Deploying...\n" | |
apigeecli "$object" deploy --wait --name "$name" --ovr --org "$ORG" --env "$ENV" --token "$TOKEN" & | |
need_wait=1 | |
else | |
printf "could not update, not importing...\n" | |
fi | |
rm -fr "$TMP_PROXY_DIR" | |
else | |
printf "could not determine name of thing to import\n" | |
fi | |
} | |
check_required_commands() { | |
local missing | |
missing=() | |
for cmd in "$@"; do | |
if ! command -v "$cmd" &>/dev/null; then | |
missing+=("$cmd") | |
fi | |
done | |
if [[ -n "$missing" ]]; then | |
printf -v joined '%s,' "${missing[@]}" | |
printf "\n\nThese commands are missing; they must be available on path: %s\nExiting.\n" "${joined%,}" | |
exit 1 | |
fi | |
} | |
check_shell_variables() { | |
local MISSING_ENV_VARS | |
MISSING_ENV_VARS=() | |
for var_name in "$@"; do | |
if [[ -z "${!var_name}" ]]; then | |
MISSING_ENV_VARS+=("$var_name") | |
fi | |
done | |
[[ ${#MISSING_ENV_VARS[@]} -ne 0 ]] && { | |
printf -v joined '%s,' "${MISSING_ENV_VARS[@]}" | |
printf "You must set these environment variables: %s\n" "${joined%,}" | |
exit 1 | |
} | |
printf "Settings in use:\n" | |
for var_name in "${env_vars_to_check[@]}"; do | |
printf " %s=%s\n" "$var_name" "${!var_name}" | |
done | |
} | |
# ==================================================================== | |
PUTATIVE_DIR=$1 | |
if ! [[ -d "$PUTATIVE_DIR" ]]; then | |
printf "specify a directory containing the bundle as the first argument\n" | |
exit 1 | |
fi | |
check_shell_variables "${env_vars_to_check[@]}" | |
check_required_commands cp rm mktemp tar shasum xsltproc apigeecli | |
TOKEN=$(gcloud auth print-access-token) | |
# import the bundle (Sharedflow or apiproxy) | |
import_and_deploy_with_shasum "$PUTATIVE_DIR" | |
if [[ $need_wait -eq 1 ]]; then | |
printf "Waiting...\n" | |
wait | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment