162 lines
7.7 KiB
Bash
162 lines
7.7 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=lib/env.sh
|
|
source "$HERE/lib/env.sh"
|
|
|
|
ENV_ALIAS="${NDO_ENV:-}"
|
|
|
|
# -e/--env may appear anywhere; strip it before dispatch.
|
|
ARGS=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-e|--env) ENV_ALIAS="$2"; shift 2 ;;
|
|
*) ARGS+=("$1"); shift ;;
|
|
esac
|
|
done
|
|
set -- "${ARGS[@]:-}"
|
|
|
|
NDO_REALM="${NDO_REALM:-default}"
|
|
NDO_CLIENT="${NDO_CLIENT:-frontend}"
|
|
NDO_USER="${NDO_USER:?Set NDO_USER through an approved configuration source before using authenticated API commands}"
|
|
NDO_PASS="${NDO_PASS:?Set NDO_PASS through an approved secret source before using authenticated API commands}"
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
ndo-api.sh — drive the NDO BOM API for live repro, on any registered env.
|
|
|
|
Every command needs a target env: -e <alias> (or NDO_ENV=<alias>).
|
|
Auth is automatic: a keycloak password-grant token is minted per env and
|
|
refreshed on expiry (~15 min). Token cache: ~/.cache/ndo-repro/token-<env>.
|
|
|
|
Env:
|
|
env ls list registered envs
|
|
env discover rescan kube contexts, rebuild the registry
|
|
env show resolved context / namespace / gateway for -e
|
|
|
|
API:
|
|
login mint a fresh token now
|
|
token <jwt> save an externally-supplied bearer token
|
|
whoami check auth (200 = ok)
|
|
opdef <key> GET operation-definition for an op key
|
|
initiate <key> [bodyfile] POST initiate, prints operation-request-id
|
|
perform <rid> <innerJsonOrFile> POST /{rid}/perform with a wrapped {method,url,body}
|
|
prepare <rid> <key> <bodyfile> POST /{rid}/prepare?key=<key> with body file
|
|
get <rid> <cimPath> [innerBodyJson] perform a GET against /consolidated-inventory-manager<cimPath>
|
|
report <rid> replacement report (resolved/unresolved)
|
|
target <rid> replacement target tree
|
|
avail <rid> <impactMkey> <refMkey> [type] available-target-values (type default l2_link)
|
|
|
|
Cluster:
|
|
logs <service> [since] [grep] tail+denoise logs (default since=10m)
|
|
image <service> deployed image of <service>-v1
|
|
pods <service> pod phase/restarts for <service>-v1
|
|
|
|
Examples:
|
|
ndo-api.sh env ls
|
|
ndo-api.sh -e shared-244 whoami
|
|
ndo-api.sh -e oss-01/dev-2 report 21dec51b-f9cb-41fe-af94-512c0921036b
|
|
ndo-api.sh -e oss-01/dev-2 logs consolidated-inventory-manager 15m '\[UNM-231239\]'
|
|
USAGE
|
|
}
|
|
|
|
case "${1:-}" in
|
|
""|-h|--help|help) usage; exit 0 ;;
|
|
env)
|
|
case "${2:-ls}" in
|
|
ls|list) env_list; exit 0 ;;
|
|
discover) env_discover; exit 0 ;;
|
|
show) env_resolve "$ENV_ALIAS"; printf 'alias : %s\ncontext : %s\nns : %s\ngateway : %s\n' \
|
|
"$ENV_ALIAS" "$NDO_CTX" "$NDO_NS" "$NDO_GW"; exit 0 ;;
|
|
*) echo "env: ls | discover | show" >&2; exit 2 ;;
|
|
esac ;;
|
|
esac
|
|
|
|
env_resolve "$ENV_ALIAS"
|
|
GW="${NDO_GW_OVERRIDE:-$NDO_GW}"
|
|
BOM="$GW/business-operation-manager/v1"
|
|
mkdir -p "$NDO_CACHE"
|
|
TOKFILE="${NDO_TOKEN_FILE:-$NDO_CACHE/token-$(tr '/' '_' <<<"$ENV_ALIAS").txt}"
|
|
|
|
mint() {
|
|
local out
|
|
out=$(curl -sk -X POST "$GW/auth/realms/$NDO_REALM/protocol/openid-connect/token" \
|
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
--data-urlencode "grant_type=password" --data-urlencode "client_id=$NDO_CLIENT" \
|
|
--data-urlencode "username=$NDO_USER" --data-urlencode "password=$NDO_PASS")
|
|
printf '%s' "$out" | python3 -c "import sys,json;d=json.load(sys.stdin);open('$TOKFILE','w').write(d['access_token']) if 'access_token' in d else sys.exit('mint failed: '+json.dumps(d)[:200])" || return 1
|
|
chmod 600 "$TOKFILE"
|
|
}
|
|
|
|
token_valid() {
|
|
[ -s "$TOKFILE" ] || return 1
|
|
python3 - "$TOKFILE" <<'PY' 2>/dev/null
|
|
import sys,base64,json,time
|
|
t=open(sys.argv[1]).read().strip()
|
|
p=t.split('.')[1]; p+='='*(-len(p)%4)
|
|
exp=json.loads(base64.urlsafe_b64decode(p)).get('exp',0)
|
|
sys.exit(0 if exp-time.time()>30 else 1)
|
|
PY
|
|
}
|
|
|
|
ensure_token() { token_valid || mint; }
|
|
tok() { cat "$TOKFILE"; }
|
|
auth() { ensure_token >&2 || { echo "auth failed on $ENV_ALIAS" >&2; exit 1; }; echo "Authorization: Bearer $(tok)"; }
|
|
K() { kubectl --context="$NDO_CTX" -n "$NDO_NS" "$@"; }
|
|
|
|
# Services use either app=<svc>-v1 or name=<svc>-v1 depending on the chart.
|
|
selector_for() {
|
|
local svc="$1" l
|
|
for l in "app=$svc-v1" "name=$svc-v1" "app=$svc" "name=$svc"; do
|
|
[ -n "$(K get pod -l "$l" -o name 2>/dev/null)" ] && { echo "$l"; return 0; }
|
|
done
|
|
echo "no pods for $svc (tried app=/name= selectors) in $NDO_NS" >&2
|
|
return 1
|
|
}
|
|
|
|
case "${1:-}" in
|
|
token) printf '%s' "$2" > "$TOKFILE"; chmod 600 "$TOKFILE"; echo "saved to $TOKFILE"; ;;
|
|
login) mint && echo "minted ($NDO_USER, realm=$NDO_REALM, env=$ENV_ALIAS) → $TOKFILE" ;;
|
|
whoami) curl -sk -o /dev/null -w "HTTP %{http_code}\n" -H "$(auth)" "$BOM/operation-definition?key=nc_op_ci_as-is_hw-component.replacement" ;;
|
|
opdef) curl -sk -H "$(auth)" "$BOM/operation-definition?key=$2" ;;
|
|
initiate)
|
|
body="${3:-{} }"; [ -f "${3:-}" ] && body="@$3"
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/initiate?key=$2" -d "$body" ;;
|
|
perform)
|
|
inner="$3"; [ -f "$3" ] && inner="@$3"
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$2/perform" -d "$inner" ;;
|
|
prepare)
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$2/prepare?key=$3" -d "@$4" ;;
|
|
get)
|
|
rid="$2"; path="$3"; innerbody="${4:-}"
|
|
if [ -n "$innerbody" ]; then req="{\"method\":\"GET\",\"url\":\"/consolidated-inventory-manager$path\",\"body\":$innerbody}";
|
|
else req="{\"method\":\"GET\",\"url\":\"/consolidated-inventory-manager$path\"}"; fi
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$rid/perform" -d "$req" ;;
|
|
report)
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$2/perform" \
|
|
-d '{"method":"GET","url":"/consolidated-inventory-manager/v3/replacement/report"}' \
|
|
| python3 -c "import sys,json;i=json.load(sys.stdin).get('action-report',{}).get('results',{}).get('impact',[]);print(json.dumps(i,indent=1))" ;;
|
|
target)
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$2/perform" \
|
|
-d '{"method":"GET","url":"/consolidated-inventory-manager/v3/replacement/target"}' ;;
|
|
avail)
|
|
typ="${5:-l2_link}"
|
|
curl -sk -X POST -H "$(auth)" -H 'Content-Type: application/json' "$BOM/operation-request/$2/perform" \
|
|
-d "{\"method\":\"GET\",\"url\":\"/consolidated-inventory-manager/v3/replacement/mapping/available-target-values\",\"body\":{\"impact-type\":\"$typ\",\"impacted-entity-mkey\":\"$3\",\"ref-endpoint-mkey\":\"$4\"}}" \
|
|
| python3 -c "import sys,json;r=json.load(sys.stdin).get('action-report',{}).get('results',{});print('total',r.get('total'),'values',len(r.get('available-values',[])))" ;;
|
|
logs)
|
|
svc="$2"; since="${3:-10m}"; pat="${4:-}"
|
|
SEL=$(selector_for "$svc") || exit 1
|
|
P=$(K get pod -l "$SEL" -o jsonpath='{.items[0].metadata.name}')
|
|
K logs "$P" --since="$since" 2>/dev/null \
|
|
| sed -E 's/\[(tenant_id|thread|originating_bi_id|traceId|spanId|request_id)=[^]]*\] ?//g' \
|
|
| { [ -n "$pat" ] && grep -aE "$pat" || cat; } ;;
|
|
image)
|
|
K get deploy "$2-v1" -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}' ;;
|
|
pods)
|
|
SEL=$(selector_for "$2") || exit 1
|
|
K get pod -l "$SEL" -o custom-columns='POD:.metadata.name,PHASE:.status.phase,READY:.status.containerStatuses[0].ready,RESTARTS:.status.containerStatuses[0].restartCount,IMAGE:.status.containerStatuses[0].image' ;;
|
|
*) echo "unknown cmd: $1"; usage; exit 1 ;;
|
|
esac
|