Skip to content

Instantly share code, notes, and snippets.

@brianthelion
Forked from wheresjames/README.md
Last active October 28, 2020 21:44
Show Gist options
  • Save brianthelion/acdb120a61e421a0f7834b6d00c736c3 to your computer and use it in GitHub Desktop.
Save brianthelion/acdb120a61e421a0f7834b6d00c736c3 to your computer and use it in GitHub Desktop.
Publishing a static HTML site on GCP, including SSL

Note

This gist is a fork. I found OP's gsite.sh code below informative, but ultimately didn't use it. See my steps.sh.

Links

NOTE

While all of the command line calls return almost immediately, some of the changes on the GCP side take MANY MINUTES to complete. In the meantime, you may see errors. That doesn't necessarily mean that you screwed something up.

#!/bin/bash
#--------------------------------------------------------------------
# Created by Robert Umbehant
#--------------------------------------------------------------------
#
# https://cloud.google.com/appengine/docs/standard/python/getting-started/hosting-a-static-website
#
#--------------------------------------------------------------------
#
# List users
# $ gcloud.sh login-list
#
# Create new login credentials
# $ gcloud.sh login
#
# Switch to another existing user
# $ gcloud.sh login-switch <username>
#
# Revoke user credentials
# $ gcloud.sh revoke <username>
#
# Set project (converts website.com to project name website-com)
# $ gcloud.sh setproject <website.com>
#
# Compress / Publish
# $ gcloud.sh compress-publish <website.com> /var/www/html/website
#
# Create load balancer
# $ gcloud.sh ip-ssl-loadbalancer-create-list example.com
#
#--------------------------------------------------------------------
# Usage
USAGE="USAGE: gcloud.sh <cmd>"
#--------------------------------------------------------------------
# Functions
#--------------------------------------------------------------------
isCmd()
{
# - separator
FOUND=$(echo "\-$COMMANDLIST-" | grep -o "\-${1}-")
if [[ ! -z $FOUND ]]; then return 0; fi
# , separator
FOUND=$(echo ",$COMMANDLIST," | grep -o ",${1},")
if [[ ! -z $FOUND ]]; then return 0; fi
return -1
}
findInStr()
{
FIND_LIST=$1
FIND_EXISTS=$(echo $FIND_LIST | grep -o $2)
if [[ ! -z $FIND_EXISTS ]]; then return 0; fi
return -1
}
findIn()
{
findInStr "$($1)" $2
return $?
}
showWarning()
{
if [[ 0 -lt ${#@} ]]; then
echo -e "[\e[1;33mWARNING\e[1;0m] \e[1;33m${@}\e[1;0m"
fi
}
exitWithError()
{
if [[ 0 -lt ${#@} ]]; then
echo
echo "--------------------------------------------------------------------"
echo -e "[\e[1;31mERROR\e[1;0m] \e[1;31m${@}\e[1;0m"
echo "--------------------------------------------------------------------"
echo
fi
exit -1
}
exitOnError()
{
if [[ 0 -eq $? ]]; then return 0; fi
exitWithError $@
}
warnOnError()
{
if [[ 0 -eq $? ]]; then return 0; fi
showWarning $@
}
isOnline()
{
wget -q --tries=1 --timeout=8 --spider http://google.com
return $?
}
# Download specified file
# @param $1 - File name
# @param $2 - URL Link
# @returns Path to tool
downloadTool()
{
# Tool path
if [ ! -d "$TOOLPATH" ]; then
mkdir -p "$TOOLPATH"
exitOnError "Failed to create path : $TOOLPATH"
fi
# Final tool path
TOOLDNL="${TOOLPATH}/${1}"
# Download the tool if we don't have it
if [ ! -f $TOOLDNL ]; then
echo "Downloading $1..."
echo
curl -L $2 -o $TOOLDNL
exitOnError "CURL failed to download $1"
if [ ! -f $TOOLDNL ]; then
exitWithError "Failed to download $1"
fi
fi
}
# @param $1 - Tool file name
# @param $2 - grep pattern to find exec in zip
# @param $3 - URL to zip file
downloadToolCompressed()
{
TOOLEXEC="${TOOLPATH}/$1"
# Already exists?
if [ -f "${TOOLEXEC}" ]; then return 0; fi
TOOLEXT="${3##*.}"
# Remove existing
TMPZIP="${TOOLPATH}/tmp.${TOOLEXT}"
if [ -f "$TMPZIP" ]; then
rm "$TMPZIP"
fi
# Download the archive
downloadTool "tmp.${TOOLEXT}" $3
if [ ! -f "$TMPZIP" ]; then
exitWithError "Failed to download tool $1"
fi
# Lose old path
TMPZIPPATH="${TOOLPATH}/tmp-${TOOLEXT}"
if [ -d "$TMPZIPPATH" ]; then
rm -Rf "$TMPZIPPATH"
fi
# Create path to extract files
mkdir -p "$TMPZIPPATH"
exitOnError "Failed to create path : $TMPZIPPATH"
case ${TOOLEXT,,} in
"zip")
unzip "$TMPZIP" -d "$TMPZIPPATH"
;;
*)
exitWithError "Unknown archive type : ${TOOLEXT,,}"
;;
esac
# Find the file
TOOLFIND=$(find $TMPZIPPATH | grep -E $2 | head -1)
if [ -z $TOOLFIND ] || [ ! -f $TOOLFIND ]; then
exitWithError "Failed to find in archive : $2 -> $1"
fi
# Copy the exe we found
mv "$TOOLFIND" "$TOOLEXEC"
# Cleanup
rm "$TMPZIP"
rm -Rf "$TMPZIPPATH"
}
# Download compression tools
initCompress()
{
JAVA=$(which java)
if [ -z "$JAVA" ] || [ ! -f "$JAVA" ]; then
exitOnError "Java not installed"
fi
# Closer compiler
downloadToolCompressed "cc.jar" "closure-compiler" "https://dl.google.com/closure-compiler/compiler-latest.zip"
CCEXEC=$TOOLEXEC
# HTML Minifier
MINEXEC=$(which html-minifier)
if [ -z "$MINEXEC" ] || [ ! -f "$MINEXEC" ]; then
npm install html-minifier -g
exitOnError "Failed to install html-minifier"
fi
MINEXEC=$(which html-minifier)
if [ -z "$MINEXEC" ] || [ ! -f "$MINEXEC" ]; then
exitOnError "Failed to install html-minifier"
fi
downloadTool "yui.jar" "https://github.com/yui/yuicompressor/releases/download/v2.4.8/yuicompressor-2.4.8.jar"
YUIEXEC=$TOOLDNL
if [ -z "$TOOLDNL" ] || [ ! -f "$TOOLDNL" ]; then
exitOnError "Failed to install html-minifier"
fi
echo
echo "--------------------------------------------------------------------"
echo "- JAVA : $JAVA"
echo "- CLOSURE COMPILER : $CCEXEC"
echo "- HTML MINIFIER : $MINEXEC"
echo "- CSS MINIFIER : $YUIEXEC"
echo "--------------------------------------------------------------------"
echo
}
# Compresses a web site
# @param $1 - Input directory
# @param $2 - Output directory
compressWeb()
{
echo "- Compressing : $1"
if [[ $1 =~ ".." ]]; then
exitOnError "Invalid directory : $1"
fi
if [ ! -d "$1" ]; then
exitOnError "Directory doesn't exist : $1"
fi
if [ ! -d "$2" ]; then
mkdir -p $2
exitOnError "Failed to create directory : $2"
fi
FILES=$1/*
for SRC in $FILES
do
FNAME=`basename $SRC`
TGT=$2/$FNAME
if [ -d $SRC ]; then
compressWeb $SRC $TGT
# Is the directory empty?
elif [[ $SRC =~ "*" ]]; then
echo "Empty directory : $SRC -> $TGT"
# Is it already minimized?
elif [[ $SRC =~ ".min." ]]; then
echo "Copy Minimized File : $SRC -> $TGT"
cp "$SRC" "$TGT"
exitOnError "(d) Failed to copy $SRC -> $TGT"
# Process this file
else
EXT="${FNAME##*.}"
echo "$EXT : $SRC -> $TGT"
case ${EXT,,} in
"js")
$JAVA -jar $CCEXEC --js_output_file "$TGT" --js "$SRC"
if [[ 0 -ne $? ]]; then
echo "!!! Failed to build $SRC"
cp "$SRC" "$TGT"
exitOnError "(a) Failed to copy $SRC -> $TGT"
fi
;;
"css")
$JAVA -jar $YUIEXEC -o "$TGT" "$SRC"
if [[ 0 -ne $? ]]; then
echo "!!! Failed to build $SRC"
cp "$SRC" "$TGT"
exitOnError "(b) Failed to copy $SRC -> $TGT"
fi
;;
"htm" | "html")
$MINEXEC --collapse-whitespace -o "$TGT" "$SRC"
if [[ 0 -ne $? ]]; then
echo "!!! Failed to build $SRC"
cp "$SRC" "$TGT"
exitOnError "(c) Failed to copy $SRC -> $TGT"
fi
;;
*)
cp "$SRC" "$TGT"
exitOnError "(d) Failed to copy $SRC -> $TGT"
;;
esac
fi
done
return 1
}
#--------------------------------------------------------------------
# Command line
#--------------------------------------------------------------------
SCRIPT_NAME=$(basename $0)
SCRIPT_PATH=$(dirname "$(readlink -f "$0")")
WORKING_PATH=$(pwd)
COMMANDLIST=$1
if [ -z $COMMANDLIST ]; then
echo $USAGE
exit 0
fi
#--------------------------------------------------------------------
# Config
#--------------------------------------------------------------------
GCLOUD="gcloud"
GSUTIL="gsutil"
# Tools
TOOLPATH="${SCRIPT_PATH}/tools"
# gcloud compute regions list
REGIONOPT='--global'
#REGION="--region us-east1"
#REGION="--region europe-north1"
# Network
NETWORKOPT=""
echo "------------------------- Configuration ----------------------------"
echo "- COMMANDS : $COMMANDLIST"
echo "- SCRIPT : $SCRIPT_NAME"
echo "- SCRIPT PATH : $SCRIPT_PATH"
echo "- WORKING PATH : $WORKING_PATH"
echo "- GCLOUD EXE : $GCLOUD"
echo "- GSUTIL EXE : $GSUTIL"
echo "- REGION : $REGIONOPT"
echo "- NETWORK : $NETWORKOPT"
echo "--------------------------------------------------------------------"
echo
$GCLOUD config list
echo
echo "--------------------------------------------------------------------"
echo
if ! isOnline; then
showWarning "You do not appear to be online"
fi
# Init gcloud
if isCmd "init"; then
echo "Initialize..."
$GCLOUD init
exit 0
fi
# Configure
if isCmd "config"; then
echo "Projects..."
$GCLOUD projects list
echo
echo "Configuration..."
$GCLOUD config list
echo
exit 0
fi
# Login
if isCmd "login"; then
if isCmd "list"; then
echo "Accounts..."
$GCLOUD auth list
elif isCmd "switch"; then
USERNAME="$2"
if [ -z $USERNAME ]; then
echo "$USAGE <username>"
echo
exitWithError "Username not specified"
fi
echo "Switch to : "
$GCLOUD config set account $USERNAME
elif isCmd "revoke"; then
USERNAME="$2"
if [ -z $USERNAME ]; then
echo "$USAGE <username>"
echo
exitWithError "Username not specified"
fi
echo "Revoke user credentials : "
$GCLOUD config revoke $USERNAME
else
echo "Login..."
$GCLOUD auth login
fi
exit 0
fi
#--------------------------------------------------------------------
# Website / project must be specified after this point
#--------------------------------------------------------------------
USAGE="$USAGE <website.com>"
WEBSITE=$2
if [ -z $WEBSITE ]; then
echo "$USAGE"
echo
exitWithError "Website / project not specified"
fi
PROJECT="${WEBSITE//./-}"
BUCKET="gs://www.${WEBSITE}"
if [ "$PROJECT" = "$WEBSITE" ]; then
exitWithError "Invalid website : $WEBSITE"
fi
echo "--------------------------------------------------------------------"
echo "- PROJECT : $PROJECT"
echo "- WEBSITE : $WEBSITE"
echo "- BUCKET : $BUCKET"
echo "--------------------------------------------------------------------"
# Set the active project
if isCmd "setproject"; then
$GCLOUD config set project ${PROJECT}
fi
# Make sure the project specified is the active project
ACTIVEPROJECT=$($GCLOUD config get-value project)
if ! findInStr $ACTIVEPROJECT "$PROJECT"; then
echo
echo "Active project: $ACTIVEPROJECT"
exitWithError "Project you specified ($PROJECT) is not active"
fi
#--------------------------------------------------------------------
# Static IP
if isCmd "ip"; then
if isCmd "create"; then
if ! findIn "gcloud compute addresses list" "${PROJECT}-ip"; then
echo "--- CREATE STATIC IP ADDRESS ---"
$GCLOUD compute addresses create ${PROJECT}-ip $REGIONOPT
exitOnError "Failed to create static IP address"
fi
fi
if isCmd "delete"; then
if findIn "gcloud compute addresses list" "${PROJECT}-ip"; then
echo "--- DELETE STATIC IP ADDRESS ---"
$GCLOUD compute addresses delete ${PROJECT}-ip
fi
fi
if isCmd "list"; then
echo "--- STATIC IP ADDRESSES ---"
$GCLOUD compute addresses list
echo
fi
fi
# See if we have an external ip
EXTERNALIP=$(gcloud compute addresses list | grep -Eo ${PROJECT}-ip.*$ | grep -Eo '([0-9]{1,3}\.?){4}')
if [ ! -z $EXTERNALIP ]; then
echo
echo "EXTERNAL IP : ${EXTERNALIP}"
echo
fi
#--------------------------------------------------------------------
# SSL
if isCmd "ssl"; then
if isCmd "create"; then
if ! findIn "$GCLOUD compute ssl-certificates list" "${PROJECT}-cert"; then
echo "--- Create SSL Certificate ---"
$GCLOUD beta compute ssl-certificates create ${PROJECT}-cert \
$REGIONOPT \
--domains ${WEBSITE},www.${WEBSITE}
exitOnError "Failed to create SSL Certificate"
fi
fi
if isCmd "delete"; then
if findIn "$GCLOUD compute ssl-certificates list" "${PROJECT}-cert"; then
echo "--- Delete SSL Certificate ---"
$GCLOUD beta compute ssl-certificates delete ${PROJECT}-cert
fi
fi
if isCmd "list"; then
echo "--- SSL Certificates ---"
$GCLOUD compute ssl-certificates list
echo
fi
fi
#--------------------------------------------------------------------
# Load balancer
if isCmd "loadbalancer"; then
if isCmd "delete"; then
#------------------------------------------------------------
# HTTP
# Delete firewall rule to let in traffic
if findIn "gcloud compute firewall-rules list" "${PROJECT}-http-fw"; then
echo "Deleting firewall rule ${PROJECT}-http-fw"
$GCLOUD compute firewall-rules delete ${PROJECT}-http-fw -q
fi
# Delete forwarding rule
if findIn "$GCLOUD compute forwarding-rules list" "${PROJECT}-http-fr"; then
echo "Deleting forwarding rule ${PROJECT}-http-fr"
$GCLOUD compute forwarding-rules delete ${PROJECT}-http-fr $REGIONOPT -q
fi
# Delete http proxy
if findIn "$GCLOUD compute target-http-proxies list" "${PROJECT}-http-proxy"; then
echo "Deleting HTTP Proxy ${PROJECT}-http-proxy"
$GCLOUD compute target-http-proxies delete ${PROJECT}-http-proxy -q
fi
#------------------------------------------------------------
# HTTPS
# Delete firewall rule to let in traffic
if findIn "gcloud compute firewall-rules list" "${PROJECT}-https-fw"; then
echo "Deleting firewall rule ${PROJECT}-https-fw"
$GCLOUD compute firewall-rules delete ${PROJECT}-https-fw -q
fi
# Delete forwarding rule
if findIn "$GCLOUD compute forwarding-rules list" "${PROJECT}-https-fr"; then
echo "Deleting forwarding rule ${PROJECT}-https-fr"
$GCLOUD compute forwarding-rules delete ${PROJECT}-https-fr $REGIONOPT -q
fi
# Delete https proxy
if findIn "$GCLOUD compute target-https-proxies list" "${PROJECT}-https-proxy"; then
echo "Deleting HTTPS Proxy ${PROJECT}-https-proxy"
$GCLOUD compute target-https-proxies delete ${PROJECT}-https-proxy -q
fi
#------------------------------------------------------------
# Delete url map
if findIn "$GCLOUD compute url-maps list" "${PROJECT}-map"; then
echo "Deleting URL Map ${PROJECT}-map"
$GCLOUD compute url-maps delete ${PROJECT}-map -q
fi
# Delete backend bucket
if findIn "$GCLOUD compute backend-buckets list" "${PROJECT}-bucket"; then
echo "Deleting bucket ${PROJECT}-bucket"
$GCLOUD compute backend-buckets delete ${PROJECT}-bucket -q
fi
fi
if isCmd "create"; then
# Create backend bucket
if ! findIn "$GCLOUD compute backend-buckets list" "${PROJECT}-bucket"; then
echo "Creating bucket ${PROJECT}-bucket"
$GCLOUD compute backend-buckets create ${PROJECT}-bucket \
--gcs-bucket-name "www.${WEBSITE}" \
--enable-cdn
exitOnError "Failed to create backend bucket"
fi
# Create url map
if ! findIn "$GCLOUD compute url-maps list" "${PROJECT}-map"; then
echo "Creating URL Map ${PROJECT}-map"
$GCLOUD compute url-maps create ${PROJECT}-map \
$REGIONOPT \
--default-backend-bucket ${PROJECT}-bucket
exitOnError "Failed to create URL map"
fi
#------------------------------------------------------------
# HTTPS
# Create https proxy
if ! findIn "$GCLOUD compute target-https-proxies list" "${PROJECT}-https-proxy"; then
echo "Creating HTTPS Proxy ${PROJECT}-https-proxy"
$GCLOUD compute target-https-proxies create ${PROJECT}-https-proxy \
--ssl-certificates ${PROJECT}-cert \
--url-map ${PROJECT}-map
exitOnError "Failed to create HTTPS Proxy"
fi
# Create forwarding rule
if ! findIn "$GCLOUD compute forwarding-rules list" "${PROJECT}-https-fr"; then
echo "Creating forwarding rule ${PROJECT}-https-fr"
$GCLOUD compute forwarding-rules create ${PROJECT}-https-fr \
$REGIONOPT \
--address $EXTERNALIP\
--target-https-proxy ${PROJECT}-https-proxy \
--ports 443
exitOnError "Failed to create forwarding rule"
fi
# Create firewall rule to let in traffic
if ! findIn "$GCLOUD compute firewall-rules list" "${PROJECT}-https-fw"; then
echo "Creating firewall rule ${PROJECT}-https-fw"
$GCLOUD compute firewall-rules create ${PROJECT}-https-fw \
--allow=udp:443,tcp:443
exitOnError "Failed to create firewall rule"
fi
#------------------------------------------------------------
# HTTP
# Create http proxy
if ! findIn "$GCLOUD compute target-http-proxies list" "${PROJECT}-http-proxy"; then
echo "Creating HTTP Proxy ${PROJECT}-http-proxy"
$GCLOUD compute target-http-proxies create ${PROJECT}-http-proxy \
--url-map ${PROJECT}-map
exitOnError "Failed to create HTTP Proxy"
fi
# Create forwarding rule
if ! findIn "$GCLOUD compute forwarding-rules list" "${PROJECT}-http-fr"; then
echo "Creating forwarding rule ${PROJECT}-http-fr"
$GCLOUD compute forwarding-rules create ${PROJECT}-http-fr \
$REGIONOPT \
--address $EXTERNALIP\
--target-http-proxy ${PROJECT}-http-proxy \
--ports 80
exitOnError "Failed to create forwarding rule"
fi
# Create firewall rule to let in traffic
if ! findIn "$GCLOUD compute firewall-rules list" "${PROJECT}-http-fw"; then
echo "Creating firewall rule ${PROJECT}-http-fw"
$GCLOUD compute firewall-rules create ${PROJECT}-http-fw \
--allow=udp:80,tcp:80
exitOnError "Failed to create firewall rule"
fi
fi
if isCmd "list"; then
echo "--- Backend buckets ---"
$GCLOUD compute backend-buckets list
echo
echo "--- URL Maps ---"
$GCLOUD compute url-maps list
echo
echo "--- HTTPS Proxies ---"
$GCLOUD compute target-https-proxies list
echo
echo "--- HTTP Proxies ---"
$GCLOUD compute target-http-proxies list
echo
echo "--- Forwarding Rules ---"
$GCLOUD compute forwarding-rules list
echo
echo "--- Firewall Rules ---"
$GCLOUD compute firewall-rules list
echo
fi
exit 0
fi
#--------------------------------------------------------------------
# Compress / Publish website
if isCmd "compress"; then
echo "Compress files..."
SRCDIR=$3
if [ -z $SRCDIR ]; then
echo "$USAGE <source-directory>"
exitWithError "Source directory not specified"
fi
if [ ! -d $SRCDIR ]; then
echo "$USAGE <source-directory>"
exitWithError "Source directory not valid : $SRCDIR"
fi
CMPDIR="$SRCDIR.min"
if [ -d $CMPDIR ]; then
rm -Rf "$CMPDIR"
#exitWithError "Compressed directory already exists : $CMPDIR"
fi
# Get tools
initCompress
# Compress the web files
compressWeb "$SRCDIR" "$CMPDIR"
if [ ! -d "$CMPDIR" ]; then
exitWithError "Failed to compress $SRCDIR -> $CMPDIR"
fi
# Repoint to the compressed directory
SRCDIR=$CMPDIR
fi
if isCmd "publish"; then
echo "Publish files..."
# Set project
$GCLOUD config set project ${PROJECT}
# Ensure project bucket exists
if ! findIn "${GSUTIL} ls" "${BUCKET}"; then
echo "Creating project bucket..."
# Create bucket
$GSUTIL mb "${BUCKET}"
# Set public read access
$GSUTIL defacl set public-read "${BUCKET}"
# Set default pages
$GSUTIL web set -m "index.html" -e "404.html" "${BUCKET}"
echo "Create a DNS Record : www.${WEBSITE} CNAME c.storage.googleapis.com"
fi
# List buckets
echo "--- Buckets ---"
$GSUTIL ls
# If source not already specified (could be compressed)
if [ -z $SRCDIR ]; then
SRCDIR=$3
if [ -z $SRCDIR ]; then
echo "$USAGE <source-directory>"
exitWithError "Source directory not specified"
fi
fi
if [ ! -d $SRCDIR ]; then
echo "$USAGE <source-directory>"
exitWithError "Source directory not valid : $SRCDIR"
fi
# Sync files
$GSUTIL -m rsync -r "$SRCDIR" "${BUCKET}"
fi
TMPDIR=$(mktemp -d)
echo "It worked" > ${TMPDIR}/index.html
gsutil mb -b on gs://${PREFIX}
gsutil rsync -R ${TMPDIR} gs://${PREFIX}
gsutil web set -m index.html gs://${PREFIX}
gsutil iam ch allUsers:objectViewer gs://${PREFIX}
gcloud compute addresses create \
${PREFIX} \
--network-tier=PREMIUM \
--ip-version=IPV4 \
--global
IP=$(gcloud compute addresses describe \
${PREFIX} \
--format="get(address)" \
--global)
gcloud compute backend-buckets create \
${PREFIX} \
--gcs-bucket-name=${PREFIX}
gcloud compute ssl-certificates create \
${PREFIX} \
--description="For https://${DOMAIN}" \
--domains=${DOMAIN} \
--global
gcloud compute ssl-certificates list \
--global
gcloud compute url-maps create \
${PREFIX}-http \
--default-backend-bucket=${PREFIX}
gcloud compute target-http-proxies create \
${PREFIX}-lb-proxy \
--url-map=${PREFIX}-http
gcloud compute forwarding-rules create \
${PREFIX}-fwd-http \
--address=${IP} \
--global \
--target-http-proxy=${PREFIX}-lb-proxy \
--ports=80
# At this point, you need to update your DNS to point ${DOMAIN} at ${IP} as an A RECORD or SSL cert creation will fail.
gcloud compute ssl-certificates create \
${PREFIX} \
--description="For https://${DOMAIN}" \
--domains=${DOMAIN} \
--global
gcloud compute target-https-proxies create \
${PREFIX}-lb-proxy-https \
--url-map=${PREFIX}-http \
--ssl-certificates=${PREFIX}
gcloud compute forwarding-rules create \
${PREFIX}-fwd-https \
--address=${IP} \
--global \
--target-https-proxy=${PREFIX}-lb-proxy-https \
--ports=443
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment