|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +# This script checks if the configured Service Account has write permissions to the specified GCS bucket. |
| 4 | +# If permissions are missing, it attempts to fix them by creating the SA and granting roles/storage.admin. |
| 5 | +# |
| 6 | +# Expected Environment Variables: |
| 7 | +# GCS_BUCKET_ROOT_DIR: The GCS path (must start with gs://) |
| 8 | +# SA_NAME: The Service Account name (default: gcs-writer) |
| 9 | +# PROJECT_ID: The GCP Project ID (optional, will try to detect if not set) |
| 10 | + |
| 11 | +SCRIPT_DIR="$(dirname "$(realpath "$0")")" |
| 12 | +SA_NAME="${SA_NAME:-gcs-writer}" |
| 13 | +PROJECT_ID="${PROJECT_ID:-$(gcloud config get-value project 2>/dev/null)}" |
| 14 | + |
| 15 | +if [[ -z "${GCS_BUCKET_ROOT_DIR}" || "${GCS_BUCKET_ROOT_DIR}" != "gs://"* ]]; then |
| 16 | + echo "Error: GCS_BUCKET_ROOT_DIR must be set and start with gs://" |
| 17 | + exit 1 |
| 18 | +fi |
| 19 | + |
| 20 | +fix_gcs_permissions() { |
| 21 | + # See more context in https://docs.cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#authenticating_to |
| 22 | + echo "Attempting to fix GCS permissions..." |
| 23 | + |
| 24 | + if [[ -z "${PROJECT_ID}" ]]; then |
| 25 | + echo "Error: PROJECT_ID is not set and could not be detected." |
| 26 | + echo "Please export PROJECT_ID=<your-project-id> and rerun." |
| 27 | + exit 1 |
| 28 | + fi |
| 29 | + |
| 30 | + local bucket_name=$(echo "${GCS_BUCKET_ROOT_DIR}" | sed 's|^gs://||' | cut -d/ -f1) |
| 31 | + local ns_name="default" |
| 32 | + |
| 33 | + echo "Ensuring ServiceAccount ${SA_NAME} exists in namespace ${ns_name}..." |
| 34 | + kubectl create serviceaccount "${SA_NAME}" --namespace "${ns_name}" --dry-run=client -o yaml | kubectl apply -f - |
| 35 | + |
| 36 | + local project_number=$(gcloud projects describe "${PROJECT_ID}" --format="value(projectNumber)") |
| 37 | + |
| 38 | + echo "Granting roles/storage.admin to ${SA_NAME} on gs://${bucket_name}..." |
| 39 | + gcloud storage buckets add-iam-policy-binding "gs://${bucket_name}" \ |
| 40 | + --role=roles/storage.admin \ |
| 41 | + --member="principal://iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${PROJECT_ID}.svc.id.goog/subject/ns/${ns_name}/sa/${SA_NAME}" |
| 42 | + |
| 43 | + echo "Permission fix command executed." |
| 44 | +} |
| 45 | + |
| 46 | +check_gcs_permission() { |
| 47 | + echo "Checking GCS write permissions..." |
| 48 | + export GCS_CHECK_PATH="${GCS_BUCKET_ROOT_DIR}/permission-check-$(date +%s).txt" |
| 49 | + export SA_NAME="${SA_NAME}" |
| 50 | + |
| 51 | + # Check if ServiceAccount exists first to fail fast |
| 52 | + if ! kubectl get serviceaccount "${SA_NAME}" &> /dev/null; then |
| 53 | + echo "ServiceAccount '${SA_NAME}' not found." |
| 54 | + return 1 |
| 55 | + fi |
| 56 | + |
| 57 | + # Launch check pod |
| 58 | + # We capture the pod name from the output of kubectl create |
| 59 | + local apply_output=$(envsubst '${SA_NAME} ${GCS_CHECK_PATH}' < "${SCRIPT_DIR}/gcs-write.yaml" | kubectl create -f -) |
| 60 | + # output example: pod/gcs-writer-test-abcde created |
| 61 | + local pod_name=$(echo "${apply_output}" | awk -F'/' '{print $2}' | awk '{print $1}') |
| 62 | + |
| 63 | + echo "Launched GCS check pod: ${pod_name}" |
| 64 | + |
| 65 | + # Wait for completion |
| 66 | + local check_status="FAILED" |
| 67 | + for i in {1..20}; do |
| 68 | + sleep 5 |
| 69 | + if kubectl get pod "${pod_name}" -o jsonpath='{.status.phase}' 2>/dev/null | grep -q "Succeeded"; then |
| 70 | + check_status="SUCCESS" |
| 71 | + break |
| 72 | + fi |
| 73 | + if kubectl get pod "${pod_name}" -o jsonpath='{.status.phase}' 2>/dev/null | grep -q "Failed"; then |
| 74 | + check_status="FAILED" |
| 75 | + break |
| 76 | + fi |
| 77 | + done |
| 78 | + |
| 79 | + # Check logs |
| 80 | + if kubectl logs "${pod_name}" 2>/dev/null | grep -q "GCS test complete!"; then |
| 81 | + echo "GCS permission check PASSED." |
| 82 | + check_status="SUCCESS" |
| 83 | + else |
| 84 | + echo "GCS permission check FAILED." |
| 85 | + check_status="FAILED" |
| 86 | + echo "Logs from ${pod_name}:" |
| 87 | + kubectl logs "${pod_name}" 2>/dev/null | tail -n 10 |
| 88 | + fi |
| 89 | + |
| 90 | + # Cleanup |
| 91 | + kubectl delete pod "${pod_name}" --grace-period=0 --force &> /dev/null |
| 92 | + |
| 93 | + if [[ "${check_status}" != "SUCCESS" ]]; then |
| 94 | + return 1 |
| 95 | + fi |
| 96 | + return 0 |
| 97 | +} |
| 98 | + |
| 99 | +# Main Logic |
| 100 | +echo "======================================================================" |
| 101 | +echo "Starting GCS Permission Check (SA: ${SA_NAME}, Bucket: ${GCS_BUCKET_ROOT_DIR})" |
| 102 | +echo "======================================================================" |
| 103 | + |
| 104 | +if ! check_gcs_permission; then |
| 105 | + echo "GCS check failed. Attempting to fix..." |
| 106 | + fix_gcs_permissions |
| 107 | + |
| 108 | + echo "Retrying GCS check..." |
| 109 | + if ! check_gcs_permission; then |
| 110 | + echo "GCS permissions check failed even after attempted fix." |
| 111 | + echo "Please verify your Service Account '${SA_NAME}' has proper permissions on ${GCS_BUCKET_ROOT_DIR}" |
| 112 | + exit 1 |
| 113 | + fi |
| 114 | +fi |
| 115 | + |
| 116 | +echo "GCS Check Verified Successfully." |
0 commit comments