← all scripts

docs/client-examples/aws_cli_examples.sh

52 of 52 statements covered (100.00%).

coveredmissednever traced by Bash (not counted)A line ending in … continues the statement above it and shares its fate.

1#!/bin/bash
2# Example: submit Kubernetes manifests to the GCO API Gateway with curl SigV4.
3#
4# Requirements:
5# - AWS CLI v2 (including `aws configure export-credentials`)
6# - curl 7.75+ with --aws-sigv4 support
7# - jq
8#
9# The AWS CLI credential provider chain is authoritative. AWS_PROFILE, SSO,
10# role assumption, web identity, environment credentials, and instance/container
11# roles are all supported; this script never reads static keys from config files.
12
137set -euo pipefail
14
1528SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
1621PROJECT_ROOT=$(cd "${SCRIPT_DIR}/../.." && pwd)
17
1821for command_name in aws curl jq; do
1921 if ! command -v "$command_name" >/dev/null 2>&1; then
201 echo "Error: required command '$command_name' is not installed" >&2
211 exit 1
22 fi
23done
24
25# Reads one value from the checkout's cdk.json, or prints the fallback when the
26# file is absent (the example was copied out of the checkout) or the key is
27# not set. jq fails on both, and its stderr is silenced because either is
28# an expected condition here, not an error.
29context_value() {
307 local jq_filter=$1
317 local fallback=$2
329 jq -er "${jq_filter} // empty" "${PROJECT_ROOT}/cdk.json" 2>/dev/null || printf '%s\n' "$fallback"
33}
34
358API_REGION=${API_REGION:-$(context_value '.context.deployment_regions.api_gateway' 'us-east-2')}
3611PROJECT_NAME=${PROJECT_NAME:-$(context_value '.context.project_name' 'gco')}
376STACK_NAME=${STACK_NAME:-${PROJECT_NAME}-api-gateway}
38
396GREEN='\033[0;32m'
406BLUE='\033[0;34m'
416RED='\033[0;31m'
426NC='\033[0m'
43
446echo -e "${BLUE}=== GCO API Gateway - curl SigV4 examples ===${NC}\n"
45
466echo -e "${GREEN}Using the active AWS identity:${NC}"
476aws sts get-caller-identity
48
496echo -e "\n${GREEN}Getting ApiEndpoint from ${STACK_NAME} in ${API_REGION}...${NC}"
50# shellcheck disable=SC2016
5112API_ENDPOINT=$(aws cloudformation describe-stacks \
52 --stack-name "$STACK_NAME" \
53 --region "$API_REGION" \
54 --query 'Stacks[0].Outputs[?OutputKey==`ApiEndpoint`].OutputValue' \
55 --output text)
566API_ENDPOINT=${API_ENDPOINT%/}
57
5812if [[ -z "$API_ENDPOINT" || "$API_ENDPOINT" == "None" ]]; then
591 echo -e "${RED}Error: ApiEndpoint was not found in stack ${STACK_NAME}${NC}" >&2
601 exit 1
61fi
62
635echo "API endpoint: ${API_ENDPOINT}"
64
65# Export the resolved credentials rather than reading static profile fields.
66# This preserves session tokens and works with profiles that assume roles or
67# source credentials from SSO, web identity, ECS, or EC2 metadata.
6810CREDENTIALS_JSON=$(aws configure export-credentials --format process)
6910ACCESS_KEY_ID=$(jq -er '.AccessKeyId' <<<"$CREDENTIALS_JSON")
7010SECRET_ACCESS_KEY=$(jq -er '.SecretAccessKey' <<<"$CREDENTIALS_JSON")
7110SESSION_TOKEN=$(jq -r '.SessionToken // empty' <<<"$CREDENTIALS_JSON")
72
735SIGV4_ARGS=(
74 --aws-sigv4 "aws:amz:${API_REGION}:execute-api"
75 --user "${ACCESS_KEY_ID}:${SECRET_ACCESS_KEY}"
76)
775if [[ -n "$SESSION_TOKEN" ]]; then
782 SIGV4_ARGS+=(--header "X-Amz-Security-Token: ${SESSION_TOKEN}")
79fi
80
81signed_curl() {
8220 curl -sS "${SIGV4_ARGS[@]}" "$@"
83}
84
855echo -e "\n${GREEN}Example 1: submit a Job manifest${NC}"
8610MANIFEST_PAYLOAD=$(cat <<'EOF'
87{
88 "manifests": [
89 {
90 "apiVersion": "batch/v1",
91 "kind": "Job",
92 "metadata": {
93 "name": "example-job",
94 "namespace": "gco-jobs"
95 },
96 "spec": {
97 "template": {
98 "spec": {
99 "containers": [
100 {
101 "name": "example",
102 "image": "busybox:1.38.0",
103 "command": ["echo", "Hello from GCO!"]
104 }
105 ],
106 "restartPolicy": "Never"
107 }
108 },
109 "backoffLimit": 3
110 }
111 }
112 ]
113}
114EOF
115)
116
11710echo "$MANIFEST_PAYLOAD" | jq '.'
11810RESPONSE=$(signed_curl \
119 -X POST "${API_ENDPOINT}/api/v1/manifests" \
120 -H "Content-Type: application/json" \
121 --data "$MANIFEST_PAYLOAD")
12210echo "$RESPONSE" | jq '.'
123
1245echo -e "\n${GREEN}Example 2: list Jobs in gco-jobs${NC}"
1255signed_curl \
126 --get "${API_ENDPOINT}/api/v1/jobs" \
127 --data-urlencode "namespace=gco-jobs" \
1285 --data-urlencode "limit=20" | jq '.'
129
1305echo -e "\n${GREEN}Example 3: inspect the submitted Job${NC}"
13110signed_curl "${API_ENDPOINT}/api/v1/jobs/gco-jobs/example-job" | jq '.'
132
1335echo -e "\n${GREEN}Example 4: validate a GPU Job without applying it${NC}"
13410GPU_MANIFEST_PAYLOAD=$(cat <<'EOF'
135{
136 "manifests": [
137 {
138 "apiVersion": "batch/v1",
139 "kind": "Job",
140 "metadata": {
141 "name": "gpu-example-job",
142 "namespace": "gco-jobs"
143 },
144 "spec": {
145 "template": {
146 "spec": {
147 "containers": [
148 {
149 "name": "gpu-example",
150 "image": "nvidia/cuda:12.0-base",
151 "command": ["nvidia-smi"],
152 "resources": {"limits": {"nvidia.com/gpu": "1"}}
153 }
154 ],
155 "restartPolicy": "Never",
156 "nodeSelector": {"karpenter.sh/capacity-type": "on-demand"},
157 "tolerations": [
158 {
159 "key": "nvidia.com/gpu",
160 "operator": "Exists",
161 "effect": "NoSchedule"
162 }
163 ]
164 }
165 },
166 "backoffLimit": 3
167 }
168 }
169 ],
170 "dry_run": true
171}
172EOF
173)
174
1755signed_curl \
176 -X POST "${API_ENDPOINT}/api/v1/manifests" \
177 -H "Content-Type: application/json" \
1785 --data "$GPU_MANIFEST_PAYLOAD" | jq '.'
179
1805echo -e "\n${BLUE}=== Examples complete ===${NC}"
1815echo "The API expects a 'manifests' array of JSON objects."
1825echo "Requests were signed with the active AWS CLI credential chain, including any session token."