Skip to content

Instantly share code, notes, and snippets.

@cig0
Last active March 26, 2021 18:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cig0/dff16c05d270cb18d481a72dc2b89c77 to your computer and use it in GitHub Desktop.
Save cig0/dff16c05d270cb18d481a72dc2b89c77 to your computer and use it in GitHub Desktop.
AWS EKS eksctl: dirty Bash script - literally, it's a very much WIP - that I put together to quickly bring up / tear down an AWS EKS test/dev cluster when working on my Harness certification
#!/usr/bin/env bash
# This script creates a small dev/test EKS cluster using Spot instances. It is very much a WIP, don't run it blindly!
# You can run ./eks.bash to get a list of the available actions; use ./eks.bash template.create to create a ClusterConfig config-file ready to spin up a new cluster
#
# Shout-outs to:
# @totallyGreg [ https://github.com/totallyGreg ]
# @Erikdeirdre [ https://github.com/erikdeirdre ]
# For sharing their code which I took as a baseline to work with and from which I learned a few tricks
## Global variables initialization
# AWS-related variables
clusterName=${clusterName:-harness-training}
vcpus=${vcpus:-2}
memory=${memory:-8}
cpu_architecture=${cpu_architecture:-x86_64}
gpus=${gpus:-0}
burst_support=${burst_support:-false}
region=${region:-us-east-2}
eks_config_file=${eks_config_file:-config-file.yaml}
k8s_version=${k8s_version:-1.19}
k8s_context=${k8s_context:-@"$clusterName.$region".eksctl.io}
# Script variables
REALPATH=$(realpath "$0")
DIRNAME=$(dirname "$REALPATH")
DATEANDTIME=$(date +"%Y-%m-%d_%H.%M.%S")
CLUSTER_LOG_NAME="$DIRNAME/$clusterName-$DATEANDTIME"
CLUSTER_LOG_NAME_CREATE="$CLUSTER_LOG_NAME-create.log"
CLUSTER_LOG_NAME_TEARDOWN="$CLUSTER_LOG_NAME-teardown.log"
_confirmAsk=', shall we proceed (YES/n):'
## Functions
# Internal functions
function _help() {
# Check profile and actions, show a help message if something's wrong
# This function needs to receive an argument to validate the conditional check for supplied actions
# when called from a callable function
# TODO: originally this function used to check just a couple of cases, as it grew over time it now needs a better logic for parsing
case ${FUNCNAME[1]} in
template.create)
[ -z "$AWS_ACCOUNT" ] &&
echo -e "\nError: missing account identifier" \
"\nPlease set the environment variable AWS_ACCOUNT with your account identifier" &&
exit 1
[ -z "$EKS_SSH_KEY" ] &&
echo -e "\nError: missing ssh key\nPlease set the environment variable EKS_KEY_SSH with a valid path/to/ssh_public_key" &&
exit 1
;;
*)
[ ! -s "$eks_config_file" ] &&
echo -e "\nError: missing eksctl configutarion file" \
"\nConfig file $eks_config_file not found, run $0 template.create to create a new one" &&
exit 1
[ -z "$AWS_PROFILE" ] &&
echo -e "\nError: missing profile id" \
"\nAWS_PROFILE environment variable not set" &&
exit 1
[ $# -eq 0 ] && echo -e "\nError: no action supplied\n" \
"Possible actions are:\n-------- ------- ---" &&
grep -E '^[[:space:]]*([[:alnum:]_]+[[:space:]]*\(\)|function[[:space:]]+([^_].*?)+[[:alnum:]_]+)' "$REALPATH" | awk -F' ' '{print $2}' &&
exit 1
# Scan this script for lines beginning with the word function, ignoring "internal" functions which begin with an underscore
;;
esac
}
function _confirm() {
# Call with a prompt string or use the default string
read -r -p "${1:-Are you sure? [YES/n]} " response
case "$response" in
[Y][E][S] | [Y])
true
;;
*)
false
;;
esac
}
function _cluster.up() {
echo "Using config file $eks_config_file"
time eksctl create cluster -f "$eks_config_file" --auto-kubeconfig 2>&1 | tee "$CLUSTER_LOG_NAME_CREATE" &&
echo "Logs written to file $CLUSTER_LOG_NAME_CREATE" &&
return
echo "There was an error while creating the cluster, check logfile $CLUSTER_LOG_NAME_CREATE for details" &&
exit 1
}
function _cluster.delete() {
_confirm "About to remove cluster $clusterName and its associated resources$_confirmAsk" &&
time eksctl delete cluster "$clusterName" 2>&1 | tee "$CLUSTER_LOG_NAME_TEARDOWN" &&
echo "Logs written to file $CLUSTER_LOG_NAME_TEARDOWN" &&
return
echo -e "\nTeardown cancelled by the user, no actions performed on the cluster" &&
exit 0
}
function _write-kubeconfig() {
eksctl utils write-kubeconfig -c "$clusterName" -r "$region" &&
echo 'K8s config updated (~/.kube/config)' &&
return ||
echo 'There was an error updating the K8s configuration file (~/.kube/config)' &&
exit 1
}
# Callable functions
function cluster.teardown() {
_help h
profile.set
time _cluster.delete &&
echo "The cluster $clusterName and its associated resources have been destroyed" &&
exit 0
}
function cluster.create() {
_help h
local iam_user
echo 'Acquiring AWS IAM UserName to setup EKS context...'
iam_user="$(aws --output=yaml iam get-user --query 'User.UserName' | sed -e 's/^"//' -e 's/"$//')"
[ -z "$iam_user" ] && echo 'There was an error retrieving current IAM user, please check your credentials an environment variables and try again' &&
exit 1 ||
echo "UserName is: $iam_user"
echo -e "\nSpinning up $clusterName EKS cluster..."
profile.set &&
time instances.get &&
time _cluster.up &&
time _write-kubeconfig &&
kubectl config use-context "$iam_user$k8s_context" &&
kubectl config set-context --current --namespace=default &&
echo "K8s context set to $iam_user$k8s_context, namespace: default" &&
exit 0
}
function instances.get() {
instances="$(ec2-instance-selector --vcpus="$vcpus" --memory="$memory" --cpu-architecture="$cpu_architecture" --gpus="$gpus" --burst-support="$burst_support" |
paste -d, -s -)"
echo "Using instances type: $instances" &&
return ||
echo 'There was an error acquiring the instances types' &&
exit 1
}
function profile.set() {
echo "AWS profile set to $AWS_PROFILE"
}
function template.create() {
_help h
local attachedPolicyARN
attachedPolicyARN=${attachedPolicyARN:-arn:aws:iam::"$AWS_ACCOUNT":policy/SecretsManager-bare}
local publicKeyPath
publicKeyPath=${publicKeyPath:-"$EKS_SSH_KEY"}
[ -f "$eks_config_file" ] &&
echo "Error: $eks_config_file already exists and will be overwritten" &&
exit 1
# TODO: this heredoc isn't fully templetized yet
cat <<EOF >"$eks_config_file"
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: $clusterName
region: $region
version: "$k8s_version"
managedNodeGroups:
- name: ng-spot
instanceTypes: ["m5.large","m5a.large","m5ad.large","m5d.large","m5dn.large","m5n.large","m5zn.large"]
desiredCapacity: 2
minSize: 2
maxSize: 2
privateNetworking: true
volumeSize: 10
ssh:
allow: true
publicKeyPath: $publicKeyPath
enableSsm: true
iam:
attachPolicyARNs:
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
- $attachedPolicyARN
volumeType: gp2
spot: true
EOF
# Check the config file was successfully created and has a size greater than zero
[ -s "$eks_config_file" ] &&
echo -e "\nConfiguration file $eks_config_file successfully created" \
"\nIf it looks good to you, run $0 cluster.create to begin creating the cluster" &&
exit 0 ||
echo "There was an error creating the configuration file $eks_config_file" &&
exit 1
}
## Run action
[ $# -eq 0 ] && _help || "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment