|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# Script to create a Kentik provisioning token for kagent Helm chart deployment |
| 4 | +# Usage: ./generate-provisioning-token.sh --name <token-name> [options] |
| 5 | +# |
| 6 | +# Kentik API (flags override environment variables): |
| 7 | +# --api-root <host> API host (or K_API_ROOT env var; default: grpc.api.kentik.com) |
| 8 | +# --api-email <email> Kentik account email (or K_API_EMAIL env var) |
| 9 | +# --api-token <token> Kentik API token (or K_API_TOKEN env var) |
| 10 | +# |
| 11 | +# Token Configuration: |
| 12 | +# --name <name> Required. User-friendly name for the token. |
| 13 | +# --max-usage <count> Max agents that can use this token (default: 1) |
| 14 | +# --expires-at <ISO-8601> Token expiration time (default: API default) |
| 15 | +# --auto-approve Skip manual approval (default: requires approval) |
| 16 | +# --site-id <id> Site ID to assign to registered agents |
| 17 | + |
| 18 | +set -euo pipefail |
| 19 | + |
| 20 | +# ============================================================================ |
| 21 | +# Defaults |
| 22 | +# ============================================================================ |
| 23 | +K_API_EMAIL="${K_API_EMAIL:-}" |
| 24 | +K_API_TOKEN="${K_API_TOKEN:-}" |
| 25 | +API_ROOT="${K_API_ROOT:-grpc.api.kentik.com}" |
| 26 | +TOKEN_NAME="" |
| 27 | +MAX_USAGE_COUNT="" |
| 28 | +EXPIRES_AT="" |
| 29 | +REQUIRES_APPROVAL="true" |
| 30 | +SITE_ID="" |
| 31 | + |
| 32 | +# ============================================================================ |
| 33 | +# Functions |
| 34 | +# ============================================================================ |
| 35 | + |
| 36 | +usage() { |
| 37 | + cat <<EOF |
| 38 | +Usage: $0 --name <token-name> [options] |
| 39 | +
|
| 40 | +Create a Kentik provisioning token for kagent Helm chart deployment. |
| 41 | +
|
| 42 | +Kentik API (flags override environment variables): |
| 43 | + --api-root <host> API host (or K_API_ROOT env var; default: grpc.api.kentik.com) |
| 44 | + --api-email <email> Kentik account email (or K_API_EMAIL env var) |
| 45 | + --api-token <token> Kentik API token (or K_API_TOKEN env var) |
| 46 | +
|
| 47 | +Token Configuration: |
| 48 | + --name <name> Required. User-friendly name for the token. |
| 49 | + --max-usage <count> Max agents that can use this token (default: 1) |
| 50 | + NOTE: must match the number of replicas you intend to deploy |
| 51 | + --expires-at <ISO-8601> Token expiration time (default: API default) |
| 52 | + --auto-approve Skip manual approval (default: requires approval) |
| 53 | + --site-id <id> Site ID to assign to registered agents |
| 54 | +
|
| 55 | +Examples: |
| 56 | + # Minimal (uses env vars for auth): |
| 57 | + export K_API_EMAIL=user@company.com |
| 58 | + export K_API_TOKEN=abc123 |
| 59 | + $0 --name "production-agents" |
| 60 | +
|
| 61 | + # Full options with CLI auth: |
| 62 | + $0 --api-root grpc.api.kentik.eu \\ |
| 63 | + --api-email user@co.com --api-token abc123 \\ |
| 64 | + --name "staging-fleet" \\ |
| 65 | + --max-usage 10 |
| 66 | +EOF |
| 67 | + exit "${1:-0}" |
| 68 | +} |
| 69 | + |
| 70 | +die() { |
| 71 | + printf 'Error: %b\n' "$1" >&2 |
| 72 | + exit 1 |
| 73 | +} |
| 74 | + |
| 75 | +check_curl() { |
| 76 | + if ! command -v curl &>/dev/null; then |
| 77 | + die "curl is required but not found. Please install curl." |
| 78 | + fi |
| 79 | +} |
| 80 | + |
| 81 | +check_jq() { |
| 82 | + if ! command -v jq &>/dev/null; then |
| 83 | + die "jq is required but not found. Please install jq." |
| 84 | + fi |
| 85 | +} |
| 86 | + |
| 87 | + |
| 88 | +build_request_body() { |
| 89 | + local body |
| 90 | + body=$(jq -n --arg name "$TOKEN_NAME" '{name: $name}') |
| 91 | + |
| 92 | + if [[ -n "$MAX_USAGE_COUNT" ]]; then |
| 93 | + body=$(echo "$body" | jq --argjson v "$MAX_USAGE_COUNT" '. + {maxUsageCount: $v}') |
| 94 | + fi |
| 95 | + |
| 96 | + if [[ -n "$EXPIRES_AT" ]]; then |
| 97 | + body=$(echo "$body" | jq --arg v "$EXPIRES_AT" '. + {expiresAt: $v}') |
| 98 | + fi |
| 99 | + |
| 100 | + if [[ "$REQUIRES_APPROVAL" == "true" ]]; then |
| 101 | + body=$(echo "$body" | jq '. + {requiresApproval: true}') |
| 102 | + else |
| 103 | + body=$(echo "$body" | jq '. + {requiresApproval: false}') |
| 104 | + fi |
| 105 | + |
| 106 | + if [[ -n "$SITE_ID" ]]; then |
| 107 | + body=$(echo "$body" | jq --arg v "$SITE_ID" '. + {config: {siteId: $v}}') |
| 108 | + fi |
| 109 | + |
| 110 | + echo "$body" |
| 111 | +} |
| 112 | + |
| 113 | +do_post() { |
| 114 | + local url="$1" |
| 115 | + local body="$2" |
| 116 | + |
| 117 | + curl -sS -w "\n%{http_code}" \ |
| 118 | + -X POST \ |
| 119 | + -H "Content-Type: application/json" \ |
| 120 | + -H "X-CH-Auth-Email: $K_API_EMAIL" \ |
| 121 | + -H "X-CH-Auth-API-Token: $K_API_TOKEN" \ |
| 122 | + -d "$body" \ |
| 123 | + "$url" |
| 124 | +} |
| 125 | + |
| 126 | +# ============================================================================ |
| 127 | +# Parse Arguments |
| 128 | +# ============================================================================ |
| 129 | + |
| 130 | +while [[ $# -gt 0 ]]; do |
| 131 | + if [[ "$1" =~ ^--(api-email|api-token|name|max-usage|expires-at|site-id|api-root)$ ]]; then |
| 132 | + if [[ $# -lt 2 || "${2:-}" == --* ]]; then |
| 133 | + die "Missing value for $1" |
| 134 | + fi |
| 135 | + fi |
| 136 | + case "$1" in |
| 137 | + --api-email) |
| 138 | + K_API_EMAIL="$2" |
| 139 | + shift 2 |
| 140 | + ;; |
| 141 | + --api-token) |
| 142 | + K_API_TOKEN="$2" |
| 143 | + shift 2 |
| 144 | + ;; |
| 145 | + --name) |
| 146 | + TOKEN_NAME="$2" |
| 147 | + shift 2 |
| 148 | + ;; |
| 149 | + --max-usage) |
| 150 | + MAX_USAGE_COUNT="$2" |
| 151 | + shift 2 |
| 152 | + ;; |
| 153 | + --expires-at) |
| 154 | + EXPIRES_AT="$2" |
| 155 | + shift 2 |
| 156 | + ;; |
| 157 | + --auto-approve) |
| 158 | + REQUIRES_APPROVAL="false" |
| 159 | + shift |
| 160 | + ;; |
| 161 | + --site-id) |
| 162 | + SITE_ID="$2" |
| 163 | + shift 2 |
| 164 | + ;; |
| 165 | + --api-root) |
| 166 | + API_ROOT="$2" |
| 167 | + shift 2 |
| 168 | + ;; |
| 169 | + --help|-h) |
| 170 | + usage 0 |
| 171 | + ;; |
| 172 | + *) |
| 173 | + die "Unknown option: $1. Use --help for usage." |
| 174 | + ;; |
| 175 | + esac |
| 176 | +done |
| 177 | + |
| 178 | +# ============================================================================ |
| 179 | +# Validate Inputs |
| 180 | +# ============================================================================ |
| 181 | + |
| 182 | +check_curl |
| 183 | +check_jq |
| 184 | + |
| 185 | +[[ -z "$K_API_EMAIL" ]] && die "Kentik email is required. Use --api-email or set K_API_EMAIL env var." |
| 186 | +[[ -z "$K_API_TOKEN" ]] && die "Kentik API token is required. Use --api-token or set K_API_TOKEN env var." |
| 187 | +[[ -z "$TOKEN_NAME" ]] && die "Token name is required. Use --name <name>." |
| 188 | + |
| 189 | +if [[ -n "$MAX_USAGE_COUNT" ]]; then |
| 190 | + if ! [[ "$MAX_USAGE_COUNT" =~ ^[0-9]+$ ]] || [[ "$MAX_USAGE_COUNT" -lt 1 ]]; then |
| 191 | + die "--max-usage must be a positive integer." |
| 192 | + fi |
| 193 | +fi |
| 194 | + |
| 195 | +# ============================================================================ |
| 196 | +# Create Token |
| 197 | +# ============================================================================ |
| 198 | + |
| 199 | +API_ROOT="${API_ROOT#https://}" |
| 200 | +API_ROOT="${API_ROOT#http://}" |
| 201 | +API_ROOT="${API_ROOT%/}" |
| 202 | +URL="https://${API_ROOT}/kagent/v202401/provisioning-tokens" |
| 203 | +BODY=$(build_request_body) |
| 204 | + |
| 205 | +echo "Creating provisioning token..." |
| 206 | +echo " API: $API_ROOT" |
| 207 | +echo " Name: $TOKEN_NAME" |
| 208 | +echo "" |
| 209 | + |
| 210 | +RESPONSE=$(do_post "$URL" "$BODY") || die "curl request failed. Check network connectivity and API host: $URL" |
| 211 | +HTTP_CODE=$(echo "$RESPONSE" | tail -1) |
| 212 | +RESPONSE_BODY=$(echo "$RESPONSE" | sed '$d') |
| 213 | + |
| 214 | +if [[ -z "$HTTP_CODE" || "$HTTP_CODE" -ne 200 ]]; then |
| 215 | + echo "API request failed (HTTP $HTTP_CODE):" >&2 |
| 216 | + if echo "$RESPONSE_BODY" | jq . >/dev/null 2>&1; then |
| 217 | + echo "$RESPONSE_BODY" | jq . >&2 |
| 218 | + else |
| 219 | + echo "$RESPONSE_BODY" >&2 |
| 220 | + fi |
| 221 | + exit 1 |
| 222 | +fi |
| 223 | + |
| 224 | +# Extract token value |
| 225 | +PROV_TOKEN=$(echo "$RESPONSE_BODY" | jq -r '.token.token') |
| 226 | +TOKEN_EXPIRES=$(echo "$RESPONSE_BODY" | jq -r '.token.expiresAt // "N/A"') |
| 227 | +TOKEN_MAX_USAGE=$(echo "$RESPONSE_BODY" | jq -r '.token.maxUsageCount // 1') |
| 228 | + |
| 229 | +if [[ -z "$PROV_TOKEN" || "$PROV_TOKEN" == "null" ]]; then |
| 230 | + die "Failed to extract token from API response. Full response:\n$RESPONSE_BODY" |
| 231 | +fi |
| 232 | + |
| 233 | +# ============================================================================ |
| 234 | +# Output |
| 235 | +# ============================================================================ |
| 236 | + |
| 237 | +echo "✓ Provisioning token created successfully!" |
| 238 | +echo "" |
| 239 | +echo " Token: $PROV_TOKEN" |
| 240 | +echo " Expires: $TOKEN_EXPIRES" |
| 241 | +echo " Max Agents: $TOKEN_MAX_USAGE" |
| 242 | +echo "" |
| 243 | +echo "Use with Helm:" |
| 244 | +echo "" |
| 245 | +echo " helm install kagent . \\" |
| 246 | +echo " --set-string kagent.companyId=YOUR_COMPANY_ID \\" |
| 247 | +echo " --set-string kagent.provisioningToken='$PROV_TOKEN'" |
| 248 | +echo "" |
0 commit comments