397ec61e57
When using kind we forward the node's port to the host, so we only really care about the `nodePort` value. For acceptance clusters, we put an Ingress in front of a NodePort Service, so we only really care about the `port` value.
316 lines
10 KiB
Bash
Executable File
316 lines
10 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# This script can be used to prepare a kind cluster and deploy the app.
|
|
# You can call this script again to redeploy the app.
|
|
# It will also output instructions on how to run the integration.
|
|
|
|
set -euo pipefail
|
|
|
|
#
|
|
# Helper functions
|
|
#
|
|
TILT_MODE=${TILT_MODE:-no}
|
|
function tilt_mode() {
|
|
if [[ "$TILT_MODE" == "yes" ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
function log_note() {
|
|
GREEN='\033[0;32m'
|
|
NC='\033[0m'
|
|
if [[ ${COLORTERM:-unknown} =~ ^(truecolor|24bit)$ ]]; then
|
|
echo -e "${GREEN}$*${NC}"
|
|
else
|
|
echo "$*"
|
|
fi
|
|
}
|
|
|
|
function log_error() {
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
if [[ ${COLORTERM:-unknown} =~ ^(truecolor|24bit)$ ]]; then
|
|
echo -e "🙁${RED} Error: $* ${NC}"
|
|
else
|
|
echo ":( Error: $*"
|
|
fi
|
|
}
|
|
|
|
function check_dependency() {
|
|
if ! command -v "$1" >/dev/null; then
|
|
log_error "Missing dependency..."
|
|
log_error "$2"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Handle argument parsing and help message
|
|
#
|
|
help=no
|
|
skip_build=no
|
|
clean_kind=no
|
|
|
|
while (("$#")); do
|
|
case "$1" in
|
|
-h | --help)
|
|
help=yes
|
|
shift
|
|
;;
|
|
-s | --skip-build)
|
|
skip_build=yes
|
|
shift
|
|
;;
|
|
-c | --clean)
|
|
clean_kind=yes
|
|
shift
|
|
;;
|
|
-*)
|
|
log_error "Unsupported flag $1" >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
log_error "Unsupported positional arg $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ "$help" == "yes" ]]; then
|
|
me="$(basename "${BASH_SOURCE[0]}")"
|
|
log_note "Usage:"
|
|
log_note " $me [flags]"
|
|
log_note
|
|
log_note "Flags:"
|
|
log_note " -h, --help: print this usage"
|
|
log_note " -s, --skip-build: reuse the most recently built image of the app instead of building"
|
|
exit 1
|
|
fi
|
|
|
|
pinniped_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$pinniped_path" || exit 1
|
|
|
|
#
|
|
# Check for dependencies
|
|
#
|
|
check_dependency docker "Please install docker. See https://docs.docker.com/get-docker"
|
|
check_dependency kind "Please install kind. e.g. 'brew install kind' for MacOS"
|
|
check_dependency ytt "Please install ytt. e.g. 'brew tap k14s/tap && brew install ytt' for MacOS"
|
|
check_dependency kapp "Please install kapp. e.g. 'brew tap k14s/tap && brew install kapp' for MacOS"
|
|
check_dependency kubectl "Please install kubectl. e.g. 'brew install kubectl' for MacOS"
|
|
check_dependency htpasswd "Please install htpasswd. Should be pre-installed on MacOS. Usually found in 'apache2-utils' package for linux."
|
|
|
|
# Require kubectl >= 1.18.x
|
|
if [ "$(kubectl version --client=true --short | cut -d '.' -f 2)" -lt 18 ]; then
|
|
log_error "kubectl >= 1.18.x is required, you have $(kubectl version --client=true --short | cut -d ':' -f2)"
|
|
exit 1
|
|
fi
|
|
|
|
if ! tilt_mode; then
|
|
if [[ "$clean_kind" == "yes" ]]; then
|
|
log_note "Deleting running kind clusters to prepare from a clean slate..."
|
|
kind delete cluster --name pinniped
|
|
fi
|
|
|
|
#
|
|
# Setup kind and build the app
|
|
#
|
|
log_note "Checking for running kind clusters..."
|
|
if ! kind get clusters | grep -q -e '^pinniped$'; then
|
|
log_note "Creating a kind cluster..."
|
|
# single-node.yaml exposes node port 31234 as 127.0.0.1:12345 and port 31235 as 127.0.0.1:12346
|
|
kind create cluster --config "$pinniped_path/hack/lib/kind-config/single-node.yaml" --name pinniped
|
|
else
|
|
if ! kubectl cluster-info | grep master | grep -q 127.0.0.1; then
|
|
log_error "Seems like your kubeconfig is not targeting a local cluster."
|
|
log_error "Exiting to avoid accidentally running tests against a real cluster."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
registry="docker.io"
|
|
repo="test/build"
|
|
registry_repo="$registry/$repo"
|
|
tag=$(uuidgen) # always a new tag to force K8s to reload the image on redeploy
|
|
|
|
if [[ "$skip_build" == "yes" ]]; then
|
|
most_recent_tag=$(docker images "$repo" --format "{{.Tag}}" | head -1)
|
|
if [[ -n "$most_recent_tag" ]]; then
|
|
tag="$most_recent_tag"
|
|
do_build=no
|
|
else
|
|
# Oops, there was no previous build. Need to build anyway.
|
|
do_build=yes
|
|
fi
|
|
else
|
|
do_build=yes
|
|
fi
|
|
|
|
registry_repo_tag="${registry_repo}:${tag}"
|
|
|
|
if [[ "$do_build" == "yes" ]]; then
|
|
# Rebuild the code
|
|
log_note "Docker building the app..."
|
|
docker build . --tag "$registry_repo_tag"
|
|
fi
|
|
|
|
# Load it into the cluster
|
|
log_note "Loading the app's container image into the kind cluster..."
|
|
kind load docker-image "$registry_repo_tag" --name pinniped
|
|
|
|
manifest=/tmp/manifest.yaml
|
|
|
|
#
|
|
# Deploy local-user-authenticator
|
|
#
|
|
pushd deploy/local-user-authenticator >/dev/null
|
|
|
|
log_note "Deploying the local-user-authenticator app to the cluster..."
|
|
ytt --file . \
|
|
--data-value "image_repo=$registry_repo" \
|
|
--data-value "image_tag=$tag" >"$manifest"
|
|
|
|
kubectl apply --dry-run=client -f "$manifest" # Validate manifest schema.
|
|
kapp deploy --yes --app local-user-authenticator --diff-changes --file "$manifest"
|
|
|
|
popd >/dev/null
|
|
|
|
#
|
|
# Deploy dex
|
|
#
|
|
pushd test/deploy/dex >/dev/null
|
|
|
|
log_note "Deploying Dex to the cluster..."
|
|
ytt --file . >"$manifest"
|
|
kubectl apply --dry-run=client -f "$manifest" # Validate manifest schema.
|
|
kapp deploy --yes --app dex --diff-changes --file "$manifest"
|
|
|
|
popd >/dev/null
|
|
fi
|
|
|
|
test_username="test-username"
|
|
test_groups="test-group-0,test-group-1"
|
|
set +o pipefail
|
|
test_password="$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-z0-9' | fold -w 32 | head -n 1)"
|
|
set -o pipefail
|
|
if [[ ${#test_password} -ne 32 ]]; then
|
|
log_error "Could not create test user's random password"
|
|
exit 1
|
|
fi
|
|
log_note "Creating test user '$test_username'..."
|
|
kubectl create secret generic "$test_username" \
|
|
--namespace local-user-authenticator \
|
|
--from-literal=groups="$test_groups" \
|
|
--from-literal=passwordHash="$(htpasswd -nbBC 10 x "$test_password" | sed -e "s/^x://")" \
|
|
--dry-run=client \
|
|
--output yaml |
|
|
kubectl apply -f -
|
|
|
|
#
|
|
# Deploy the Pinniped Supervisor
|
|
#
|
|
supervisor_app_name="pinniped-supervisor"
|
|
supervisor_namespace="supervisor"
|
|
supervisor_custom_labels="{mySupervisorCustomLabelName: mySupervisorCustomLabelValue}"
|
|
|
|
if ! tilt_mode; then
|
|
pushd deploy/supervisor >/dev/null
|
|
|
|
log_note "Deploying the Pinniped Supervisor app to the cluster..."
|
|
ytt --file . \
|
|
--data-value "app_name=$supervisor_app_name" \
|
|
--data-value "namespace=$supervisor_namespace" \
|
|
--data-value "image_repo=$registry_repo" \
|
|
--data-value "image_tag=$tag" \
|
|
--data-value-yaml "custom_labels=$supervisor_custom_labels" \
|
|
--data-value-yaml 'service_nodeport_port=80' \
|
|
--data-value-yaml 'service_nodeport_nodeport=31234' >"$manifest"
|
|
|
|
kapp deploy --yes --app "$supervisor_app_name" --diff-changes --file "$manifest"
|
|
|
|
popd >/dev/null
|
|
fi
|
|
|
|
#
|
|
# Deploy the Pinniped Concierge
|
|
#
|
|
concierge_app_name="pinniped-concierge"
|
|
concierge_namespace="concierge"
|
|
webhook_url="https://local-user-authenticator.local-user-authenticator.svc/authenticate"
|
|
webhook_ca_bundle="$(kubectl get secret local-user-authenticator-tls-serving-certificate --namespace local-user-authenticator -o 'jsonpath={.data.caCertificate}')"
|
|
discovery_url="$(TERM=dumb kubectl cluster-info | awk '/Kubernetes master/ {print $NF}')"
|
|
concierge_custom_labels="{myConciergeCustomLabelName: myConciergeCustomLabelValue}"
|
|
|
|
if ! tilt_mode; then
|
|
pushd deploy/concierge >/dev/null
|
|
|
|
log_note "Deploying the Pinniped Concierge app to the cluster..."
|
|
ytt --file . \
|
|
--data-value "app_name=$concierge_app_name" \
|
|
--data-value "namespace=$concierge_namespace" \
|
|
--data-value-yaml "custom_labels=$concierge_custom_labels" \
|
|
--data-value "image_repo=$registry_repo" \
|
|
--data-value "image_tag=$tag" \
|
|
--data-value "discovery_url=$discovery_url" >"$manifest"
|
|
|
|
kapp deploy --yes --app "$concierge_app_name" --diff-changes --file "$manifest"
|
|
|
|
popd >/dev/null
|
|
fi
|
|
|
|
#
|
|
# Create the environment file
|
|
#
|
|
kind_capabilities_file="$pinniped_path/test/cluster_capabilities/kind.yaml"
|
|
pinniped_cluster_capability_file_content=$(cat "$kind_capabilities_file")
|
|
|
|
cat <<EOF >/tmp/integration-test-env
|
|
# The following env vars should be set before running 'go test -v -count 1 ./test/integration'
|
|
export PINNIPED_TEST_CONCIERGE_NAMESPACE=${concierge_namespace}
|
|
export PINNIPED_TEST_CONCIERGE_APP_NAME=${concierge_app_name}
|
|
export PINNIPED_TEST_CONCIERGE_CUSTOM_LABELS='${concierge_custom_labels}'
|
|
export PINNIPED_TEST_USER_USERNAME=${test_username}
|
|
export PINNIPED_TEST_USER_GROUPS=${test_groups}
|
|
export PINNIPED_TEST_USER_TOKEN=${test_username}:${test_password}
|
|
export PINNIPED_TEST_WEBHOOK_ENDPOINT=${webhook_url}
|
|
export PINNIPED_TEST_WEBHOOK_CA_BUNDLE=${webhook_ca_bundle}
|
|
export PINNIPED_TEST_SUPERVISOR_NAMESPACE=${supervisor_namespace}
|
|
export PINNIPED_TEST_SUPERVISOR_APP_NAME=${supervisor_app_name}
|
|
export PINNIPED_TEST_SUPERVISOR_CUSTOM_LABELS='${supervisor_custom_labels}'
|
|
export PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS="127.0.0.1:12345"
|
|
export PINNIPED_TEST_CLI_OIDC_ISSUER=http://127.0.0.1:12346/dex
|
|
export PINNIPED_TEST_CLI_OIDC_CLIENT_ID=pinniped-cli
|
|
export PINNIPED_TEST_CLI_OIDC_LOCALHOST_PORT=48095
|
|
export PINNIPED_TEST_CLI_OIDC_USERNAME=pinny@example.com
|
|
export PINNIPED_TEST_CLI_OIDC_PASSWORD=password
|
|
|
|
read -r -d '' PINNIPED_TEST_CLUSTER_CAPABILITY_YAML << PINNIPED_TEST_CLUSTER_CAPABILITY_YAML_EOF || true
|
|
${pinniped_cluster_capability_file_content}
|
|
PINNIPED_TEST_CLUSTER_CAPABILITY_YAML_EOF
|
|
|
|
export PINNIPED_TEST_CLUSTER_CAPABILITY_YAML
|
|
EOF
|
|
|
|
#
|
|
# Print instructions for next steps
|
|
#
|
|
goland_vars=$(grep -v '^#' /tmp/integration-test-env | grep -E '^export .+=' | sed 's/export //g' | tr '\n' ';')
|
|
|
|
log_note
|
|
log_note "🚀 Ready to run integration tests! For example..."
|
|
log_note " cd $pinniped_path"
|
|
log_note ' source /tmp/integration-test-env && go test -v -count 1 ./test/integration'
|
|
log_note
|
|
log_note 'Want to run integration tests in GoLand? Copy/paste this "Environment" value for GoLand run configurations:'
|
|
log_note " ${goland_vars}PINNIPED_TEST_CLUSTER_CAPABILITY_FILE=${kind_capabilities_file}"
|
|
log_note
|
|
|
|
if ! tilt_mode; then
|
|
log_note "You can rerun this script to redeploy local production code changes while you are working."
|
|
log_note
|
|
log_note "To delete the deployments, run:"
|
|
log_note " kapp delete -a local-user-authenticator -y && kapp delete -a $concierge_app_name -y && kapp delete -a $supervisor_app_name -y"
|
|
log_note "When you're finished, use 'kind delete cluster --name pinniped' to tear down the cluster."
|
|
fi
|