Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions deploy/gke/deploy_gke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#!/usr/bin/env bash
# =============================================================
# sifnode GKE Deployment Script
# =============================================================
# Usage:
# ./deploy_gke.sh create # Full deployment
# ./deploy_gke.sh destroy # Tear down
# ./deploy_gke.sh update # Update Helm release
# ./deploy_gke.sh status # Check deployment status
# ./deploy_gke.sh -p <project> ... # Specify GCP project
# ./deploy_gke.sh -r <region> ... # Specify region
# =============================================================

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"

# ── Defaults ──────────────────────────────────────────────────
PROJECT_ID="${GCP_PROJECT_ID:-}"
REGION="${GCP_REGION:-us-central1}"
CLUSTER_NAME="${CLUSTER_NAME:-sifnode-gke}"
NAMESPACE="${NAMESPACE:-sifnode}"
DEPLOY_ENV="${DEPLOY_ENV:-gke}"
TERRAFORM_DIR="${PROJECT_DIR}/deploy/terraform/providers/gke"
HELM_CHART="${PROJECT_DIR}/deploy/helm/sifnode"
HELM_VALUES="${PROJECT_DIR}/deploy/helm/sifnode/values_gke.yaml"
Comment on lines +16 to +27
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PROJECT_DIR is computed as the parent of deploy/gke, which makes TERRAFORM_DIR/HELM_CHART point to ${repo}/deploy/deploy/... (non-existent). This will cause cd "$TERRAFORM_DIR" and Helm installs to fail when running the script from a fresh clone. Compute the repo root (e.g., two directories up from SCRIPT_DIR, or via git rev-parse --show-toplevel) and build paths from that.

Copilot uses AI. Check for mistakes.

# ── Colors ────────────────────────────────────────────────────
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $1"; }
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
err() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }

# ── Prerequisites Check ──────────────────────────────────────
check_prereqs() {
info "Checking prerequisites..."
command -v gcloud >/dev/null 2>&1 || err "gcloud CLI not installed. Install: https://cloud.google.com/sdk/docs/install"
command -v terraform >/dev/null 2>&1 || err "Terraform not installed. Install: https://learn.hashicorp.com/tutorials/terraform/install-cli"
command -v kubectl >/dev/null 2>&1 || err "kubectl not installed."
command -v helm >/dev/null 2>&1 || err "Helm not installed."

if [[ -z "$PROJECT_ID" ]]; then
PROJECT_ID=$(gcloud config get-value project 2>/dev/null || true)
if [[ -z "$PROJECT_ID" ]]; then
err "GCP project ID not set. Use -p <project> or set GCP_PROJECT_ID env var."
fi
fi
ok "Prerequisites satisfied. Project: ${PROJECT_ID}, Region: ${REGION}"
}

# ── Terraform ─────────────────────────────────────────────────
tf_init() {
info "Initializing Terraform (GKE)..."
cd "$TERRAFORM_DIR"
terraform init -upgrade
ok "Terraform initialized"
}

tf_apply() {
info "Applying Terraform (GKE)..."
cd "$TERRAFORM_DIR"
terraform apply -auto-approve \
-var="project_id=${PROJECT_ID}" \
-var="region=${REGION}" \
-var="cluster_name=${CLUSTER_NAME}"
ok "Terraform applied successfully"
}

tf_destroy() {
warn "Destroying GKE infrastructure..."
cd "$TERRAFORM_DIR"
terraform destroy -auto-approve \
-var="project_id=${PROJECT_ID}" \
-var="region=${REGION}" \
-var="cluster_name=${CLUSTER_NAME}"
ok "GKE infrastructure destroyed"
}

# ── Kubernetes Context ───────────────────────────────────────
setup_kubectl() {
info "Configuring kubectl..."
gcloud container clusters get-credentials "$CLUSTER_NAME" \
--region "$REGION" \
--project "$PROJECT_ID"
kubectl config set-context --current --namespace="$NAMESPACE" 2>/dev/null || true
ok "kubectl configured for cluster: ${CLUSTER_NAME}"
}

# ── Helm Deployment ───────────────────────────────────────────
helm_install() {
info "Installing/Upgrading sifnode via Helm..."
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -

helm upgrade --install sifnode "$HELM_CHART" \
--namespace "$NAMESPACE" \
--create-namespace \
--values "${HELM_VALUES}" \
--set provider=gke \
--set persistence.storageClass=gke-ssd \
--wait \
--timeout 15m
ok "sifnode deployed to GKE"
}

helm_uninstall() {
info "Uninstalling sifnode Helm release..."
helm uninstall sifnode --namespace "$NAMESPACE" 2>/dev/null || true
kubectl delete namespace "$NAMESPACE" --ignore-not-found
ok "sifnode uninstalled"
}

# ── Status ────────────────────────────────────────────────────
check_status() {
info "=== GKE Cluster Status ==="
gcloud container clusters describe "$CLUSTER_NAME" \
--region "$REGION" \
--project "$PROJECT_ID" \
--format="table(name, location, status, currentMasterVersion, currentNodeVersion, currentNodeCount)"

info "=== Pod Status ==="
kubectl get pods -n "$NAMESPACE" -o wide 2>/dev/null || echo "No pods found"

info "=== Service Status ==="
kubectl get svc -n "$NAMESPACE" 2>/dev/null || echo "No services found"

info "=== Persistent Volume Claims ==="
kubectl get pvc -n "$NAMESPACE" 2>/dev/null || echo "No PVCs found"
}

# ── CLI Argument Parsing ─────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--project) PROJECT_ID="$2"; shift 2 ;;
-r|--region) REGION="$2"; shift 2 ;;
-c|--cluster) CLUSTER_NAME="$2"; shift 2 ;;
create|deploy)
check_prereqs
tf_init && tf_apply
setup_kubectl
helm_install
check_status
info "βœ… sifnode deployed to GKE successfully!"
info " Cluster: ${CLUSTER_NAME} (${REGION})"
info " Namespace: ${NAMESPACE}"
info " To check logs: kubectl logs -n ${NAMESPACE} -l app=sifnode"
exit 0
;;
destroy|teardown)
check_prereqs
helm_uninstall
tf_destroy
info "βœ… GKE deployment destroyed."
exit 0
;;
update)
check_prereqs
setup_kubectl
helm_install
check_status
exit 0
;;
status)
check_prereqs
setup_kubectl
check_status
exit 0
;;
--help|-h)
head -20 "$0" | grep "^#"
exit 0
;;
*) err "Unknown option: $1. Use --help for usage." ;;
esac
done

# Interactive mode: show menu
echo "sifnode GKE Deployment Script"
echo "1) Full deployment (create)"
echo "2) Update only (update)"
echo "3) Destroy (destroy)"
echo "4) Status (status)"
echo "Choose (1-4): "
read -r choice
case "$choice" in
1) $0 create ;;
2) $0 update ;;
3) $0 destroy ;;
4) $0 status ;;
*) err "Invalid choice" ;;
esac
6 changes: 6 additions & 0 deletions deploy/helm/sifnode/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# sifnode Helm Chart Values
# ── Provider ─────────────────────────────────────────────────
# Deployment environment: "aws" (default) or "gke"
provider: aws


50 changes: 50 additions & 0 deletions deploy/helm/sifnode/values_gke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# GKE-specific values for sifnode Helm chart
# Override with: helm install -f values_gke.yaml

# Deployment environment: "gke" or "aws" (default: aws for backward compatibility)
provider: gke

# GKE-specific persistence configuration
persistence:
enabled: true
size: 50Gi
accessMode: ReadWriteOnce
storageClass: gke-ssd # Created by GKE Terraform

# GKE node configuration
nodeSelector:
cloud.google.com/gke-nodepool: sifnode-pool
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nodeSelector.cloud.google.com/gke-nodepool is set to sifnode-pool, but the Terraform node pool is named ${cluster_name}-node-pool (e.g., sifnode-gke-node-pool). With this mismatch, pods may remain Pending due to unschedulable nodeSelector. Align the value with the actual node pool name, or remove the nodeSelector and make it configurable.

Suggested change
cloud.google.com/gke-nodepool: sifnode-pool
cloud.google.com/gke-nodepool: sifnode-gke-node-pool

Copilot uses AI. Check for mistakes.

# GKE service configuration
service:
type: LoadBalancer
port: 26656
annotations:
cloud.google.com/load-balancer-type: Internal # Change to External if needed

# GKE ingress configuration (optional)
ingress:
enabled: false
annotations:
kubernetes.io/ingress.class: gce

# GKE IAM workload identity
serviceAccount:
annotations:
iam.gke.io/gcp-service-account: "" # Set to your GCP SA email

# Resource allocation for GKE (e2-standard-2)
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "4Gi"

# GKE autoscaling
autoscaling:
enabled: true
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
Loading
Loading