From 70bffa33faa41d98d4688720201f8d9b59df4c96 Mon Sep 17 00:00:00 2001 From: Nicolas MASSE Date: Tue, 15 Jun 2021 21:59:45 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + dev.sh | 18 ++++ pack.sh | 3 + samples/01-api-with-apikey.sh | 146 +++++++++++++++++++++++++++ samples/02-cleanup.sh | 35 +++++++ samples/11-api-with-oidc.sh | 165 ++++++++++++++++++++++++++++++ samples/12-cleanup.sh | 35 +++++++ src/00-factory.sh | 43 ++++++++ src/01-common.sh | 183 ++++++++++++++++++++++++++++++++++ src/02-util.sh | 27 +++++ src/20-account.sh | 9 ++ src/22-backend_usage.sh | 25 +++++ src/23-policy.sh | 26 +++++ src/24-proxy.sh | 25 +++++ src/25-oidc-config.sh | 6 ++ 15 files changed, 747 insertions(+) create mode 100644 .gitignore create mode 100755 dev.sh create mode 100755 pack.sh create mode 100755 samples/01-api-with-apikey.sh create mode 100755 samples/02-cleanup.sh create mode 100755 samples/11-api-with-oidc.sh create mode 100755 samples/12-cleanup.sh create mode 100644 src/00-factory.sh create mode 100644 src/01-common.sh create mode 100644 src/02-util.sh create mode 100644 src/20-account.sh create mode 100644 src/22-backend_usage.sh create mode 100644 src/23-policy.sh create mode 100644 src/24-proxy.sh create mode 100644 src/25-oidc-config.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d003ce --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +3scale-admin-cli.sh diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..372165a --- /dev/null +++ b/dev.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -Eeuo pipefail + +if ! oc whoami &>/dev/null; then + echo "Not connected to OpenShift!" + exit 1 +fi + +export THREESCALE_TOKEN="$(oc get secret system-seed -o go-template --template='{{.data.ADMIN_ACCESS_TOKEN|base64decode}}')" +export ADMIN_PORTAL_HOSTNAME="$(oc get route -l zync.3scale.net/route-to=system-provider -o go-template='{{(index .items 0).spec.host}}')" +export OIDC_ISSUER_ENDPOINT="https://zync:changeme@sso-sso.apps.changeme/auth/realms/3scale" + +for f in src/*.sh; do + . "$f" +done + +# TODO \ No newline at end of file diff --git a/pack.sh b/pack.sh new file mode 100755 index 0000000..e0ff3d9 --- /dev/null +++ b/pack.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +cat src/*.sh > 3scale-admin-cli.sh diff --git a/samples/01-api-with-apikey.sh b/samples/01-api-with-apikey.sh new file mode 100755 index 0000000..1b5996c --- /dev/null +++ b/samples/01-api-with-apikey.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +set -Eeuo pipefail + +. 3scale-admin-cli.sh + +if [ -z "${THREESCALE_TOKEN:-}" ]; then + echo "Please set the THREESCALE_TOKEN environment variable!" + exit 1 +fi + +if [ -z "${ADMIN_PORTAL_HOSTNAME:-}" ]; then + echo "Please set the ADMIN_PORTAL_HOSTNAME environment variable!" + exit 1 +fi + +if [ -z "${SERVICE_NAME:-}" ]; then + echo "Please set the SERVICE_NAME environment variable!" + exit 1 +fi + +## +## Service creation +## +declare -A service_def=( ["system_name"]="$SERVICE_NAME" ["name"]="Echo API" ["description"]="Echo API" ["deployment_option"]="hosted" ["backend_version"]="1" ) +apply service present service_def +service_id=$last_object_id + +## +## Backend creation +## +declare -A backend_def=( ["system_name"]="$SERVICE_NAME" ["name"]="Echo API" ["private_endpoint"]="https://echo-api.3scale.net" ) +apply backend present backend_def +backend_id=$last_object_id + +## +## Backend method creation +## + +# Find the "hits" metric so that we can create the methods under it +breadcrumb=( backend_apis $backend_id ) +hits_metric_id="$(metric_list | id_of_external_id_with_prefix "system_name" "hits")" + +# Create one method +breadcrumb=( backend_apis $backend_id metrics $hits_metric_id ) +declare -A method_def=( ["system_name"]="say_hello" ["friendly_name"]="sayHello" ["unit"]="hits" ) +apply method present method_def +method_id=$last_object_id + +# Create one mapping rule +breadcrumb=( backend_apis $backend_id ) +delete_all "mapping_rule" +declare -A mapping_rule_def=( ["http_method"]="GET" ["pattern"]="/" ["delta"]="1" ["metric_id"]="$method_id" ) +mapping_rule_create mapping_rule_def + +## +## Proxy configuration +## +breadcrumb=( services $service_id ) +declare -A proxy_def=( ["credentials_location"]="headers" ["auth_user_key"]="X-APIKey" ) +proxy_update proxy_def +proxy="$(get_result proxy_update | cleanup_item)" + +# Unless specified in the proxy definition, the endpoints are generated by 3scale +staging_endpoint="$(echo "$proxy" | jq -r .sandbox_endpoint)" +echo "Staging endpoint is: $staging_endpoint" +production_endpoint="$(echo "$proxy" | jq -r .endpoint)" +echo "Production endpoint is: $production_endpoint" + +## +## Link the Backend to the Service +## +breadcrumb=( services $service_id ) +declare -A backend_usage_def=( ["backend_id"]="$backend_id" ["path"]="/" ) +apply backend_usage present backend_usage_def + +## +## Update the policy chain +## +breadcrumb=( services $service_id ) +policies="$(policy_list | remove_policy 'cors' | jq '. += [ {"name": "cors", "version": "builtin", "configuration": {"allow_credentials": true}, "enabled": true} ]')" +policy_update "$policies" + +## +## Create one application plan +## +breadcrumb=( services $service_id ) +declare -A application_plan_def=( ["system_name"]="test" ["name"]="Test plan" ) +apply application_plan present application_plan_def +application_plan_id=$last_object_id + +## +## Create a test application +## + +# The application can be created in the default first account (aka the "Developer" account) +account_id="$(find_first_account | jq '.id')" + +# The user_key is an external identifier for the application. To achieve +# idempotency, we need to generate one by ourself. +# +# The application name, service system_name and admin access token (secret) are +# hashed together to produce a stable but unguessable identifier. +application_name="Test app" +user_key="$(echo -n "${application_name}${SERVICE_NAME}${THREESCALE_TOKEN}" | sha1sum | cut -d " " -f1)" + +breadcrumb=( accounts $account_id ) +declare -A application_def=( ["plan_id"]="$application_plan_id" ["name"]="$application_name" ["description"]="used for internal testing" ["user_key"]="$user_key" ) +apply application_apikey present application_def +application_id=$last_object_id + +## +## Smoke tests +## + +echo "Waiting 5 seconds before running tests on the staging environment..." +sleep 5 + +if ! curl -sfk $staging_endpoint/hello -H "X-APIKey: $user_key" > /dev/null; then + echo "Smoke test failed!" + exit 1 +fi + +## +## Promotion to production +## +breadcrumb=( services $service_id ) +staging_version="$(proxy_version 'sandbox')" +echo "Proxy version in staging: $staging_version" +production_version="$(proxy_version 'production' || echo 'none')" +echo "Proxy version in production: $production_version" + +if [ "$staging_version" != "$production_version" ]; then + proxy_promote sandbox "$staging_version" +fi + +## +## Final tests +## + +while ! curl -sfk $production_endpoint/hello -H "X-APIKey: $user_key" &> /dev/null; do + echo "Production API is not yet ready..." + sleep 5 +done + +curl -sfk $production_endpoint/hello -H "X-APIKey: $user_key" --write-out "Production API - HTTP %{http_code}\n" -o /dev/null diff --git a/samples/02-cleanup.sh b/samples/02-cleanup.sh new file mode 100755 index 0000000..d62362a --- /dev/null +++ b/samples/02-cleanup.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -Eeuo pipefail + +. 3scale-admin-cli.sh + +if [ -z "${THREESCALE_TOKEN:-}" ]; then + echo "Please set the THREESCALE_TOKEN environment variable!" + exit 1 +fi + +if [ -z "${ADMIN_PORTAL_HOSTNAME:-}" ]; then + echo "Please set the ADMIN_PORTAL_HOSTNAME environment variable!" + exit 1 +fi + +if [ -z "${SERVICE_NAME:-}" ]; then + echo "Please set the SERVICE_NAME environment variable!" + exit 1 +fi + +## +## Service deletion +## +declare -A service_def=( ["system_name"]="${SERVICE_NAME}" ) +apply service absent service_def + +echo "Waiting 5 seconds before deleting the backend..." +sleep 5 + +## +## Backend deletion +## +declare -A backend_def=( ["system_name"]="${SERVICE_NAME}" ) +apply backend absent backend_def diff --git a/samples/11-api-with-oidc.sh b/samples/11-api-with-oidc.sh new file mode 100755 index 0000000..a19039c --- /dev/null +++ b/samples/11-api-with-oidc.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +set -Eeuo pipefail + +. 3scale-admin-cli.sh + +if [ -z "${THREESCALE_TOKEN:-}" ]; then + echo "Please set the THREESCALE_TOKEN environment variable!" + exit 1 +fi + +if [ -z "${ADMIN_PORTAL_HOSTNAME:-}" ]; then + echo "Please set the ADMIN_PORTAL_HOSTNAME environment variable!" + exit 1 +fi + +if [ -z "${OIDC_ISSUER_ENDPOINT:-}" ]; then + echo "Please set the OIDC_ISSUER_ENDPOINT environment variable!" + exit 1 +fi + +if [ -z "${SERVICE_NAME:-}" ]; then + echo "Please set the SERVICE_NAME environment variable!" + exit 1 +fi + +## +## Service creation +## +declare -A service_def=( ["system_name"]="$SERVICE_NAME" ["name"]="Echo API" ["description"]="Echo API with OIDC" ["deployment_option"]="hosted" ["backend_version"]="oidc" ) +apply service present service_def +service_id=$last_object_id + +## +## Backend creation +## +declare -A backend_def=( ["system_name"]="$SERVICE_NAME" ["name"]="Echo API" ["private_endpoint"]="https://echo-api.3scale.net" ) +apply backend present backend_def +backend_id=$last_object_id + +## +## Backend method creation +## + +# Find the "hits" metric so that we can create the methods under it +breadcrumb=( backend_apis $backend_id ) +hits_metric_id="$(metric_list | id_of_external_id_with_prefix "system_name" "hits")" + +# Create one method +breadcrumb=( backend_apis $backend_id metrics $hits_metric_id ) +declare -A method_def=( ["system_name"]="say_hello" ["friendly_name"]="sayHello" ["unit"]="hits" ) +apply method present method_def +method_id=$last_object_id + +# Create one mapping rule +breadcrumb=( backend_apis $backend_id ) +delete_all "mapping_rule" +declare -A mapping_rule_def=( ["http_method"]="GET" ["pattern"]="/" ["delta"]="1" ["metric_id"]="$method_id" ) +mapping_rule_create mapping_rule_def + +## +## Proxy configuration +## +breadcrumb=( services $service_id ) +declare -A proxy_def=( ["credentials_location"]="headers" ["oidc_issuer_endpoint"]="$OIDC_ISSUER_ENDPOINT" ) +proxy_update proxy_def +proxy="$(get_result proxy_update | cleanup_item)" + +# Unless specified in the proxy definition, the endpoints are generated by 3scale +staging_endpoint="$(echo "$proxy" | jq -r .sandbox_endpoint)" +echo "Staging endpoint is: $staging_endpoint" +production_endpoint="$(echo "$proxy" | jq -r .endpoint)" +echo "Production endpoint is: $production_endpoint" + +## +## Write the OIDC configuration +## +breadcrumb=( services $service_id ) +declare -A oidc_configuration_def=( ["standard_flow_enabled"]="false" ["implicit_flow_enabled"]="false" ["service_accounts_enabled"]="true" ["direct_access_grants_enabled"]="false" ) +oidc_configuration_update oidc_configuration_def + +## +## Link the Backend to the Service +## +breadcrumb=( services $service_id ) +declare -A backend_usage_def=( ["backend_id"]="$backend_id" ["path"]="/" ) +apply backend_usage present backend_usage_def + +## +## Update the policy chain +## +breadcrumb=( services $service_id ) +policies="$(policy_list | remove_policy 'cors' | jq '. += [ {"name": "cors", "version": "builtin", "configuration": {"allow_credentials": true}, "enabled": true} ]')" +policy_update "$policies" + +## +## Create one application plan +## +breadcrumb=( services $service_id ) +declare -A application_plan_def=( ["system_name"]="test" ["name"]="Test plan" ) +apply application_plan present application_plan_def +application_plan_id=$last_object_id + +## +## Create a test application +## + +# The application can be created in the default first account (aka the "Developer" account) +account_id="$(find_first_account | jq '.id')" + +# The application_id is an external identifier for the application. To achieve +# idempotency, we need to generate one by ourself. +# +# The application name, service system_name and admin access token (secret) are +# hashed together to produce a stable but unguessable identifier. +application_name="Test app" +client_id="$(echo -n "$application_name$SERVICE_NAME$THREESCALE_TOKEN" | sha1sum | cut -d " " -f1)" +client_secret="$(echo -n "secret$application_name$SERVICE_NAME$THREESCALE_TOKEN" | sha1sum | cut -d " " -f1)" + +breadcrumb=( accounts $account_id ) +declare -A application_def=( ["plan_id"]="$application_plan_id" ["name"]="$application_name" ["description"]="used for internal testing" ["application_id"]="$client_id" ["application_key"]="$client_secret" ) +apply application_appid present application_def +application_id=$last_object_id + +## +## Smoke tests +## + +# First, get a token from the Authorization Server +token_endpoint="$(echo "$OIDC_ISSUER_ENDPOINT" |sed -r 's|(https?)://[^:]+:[^@]+@([^/]+)/(.*)$|\1://\2/\3|')/protocol/openid-connect/token" +while ! curl -sfk "$token_endpoint" -X POST -d client_id="$client_id" -d client_secret="$client_secret" -d "grant_type=client_credentials" > "$tmp/token.json"; do + echo "Waiting for the OIDC client to appear in Keycloak..." + sleep 5 +done +token="$(jq -r .access_token "$tmp/token.json")" + +# Then, test the API +if ! curl -sfk $staging_endpoint/hello -H "Authorization: Bearer $token" > /dev/null; then + echo "Smoke test failed!" + exit 1 +fi + +## +## Promotion to production +## +breadcrumb=( services $service_id ) +staging_version="$(proxy_version 'sandbox')" +echo "Proxy version in staging: $staging_version" +production_version="$(proxy_version 'production' || echo 'none')" +echo "Proxy version in production: $production_version" + +if [ "$staging_version" != "$production_version" ]; then + proxy_promote sandbox "$staging_version" +fi + +## +## Final tests +## + +while ! curl -sfk $production_endpoint/hello -H "Authorization: Bearer $token" &> /dev/null; do + echo "Production API is not yet ready..." + sleep 5 +done + +curl -sfk $production_endpoint/hello -H "Authorization: Bearer $token" --write-out "Production API - HTTP %{http_code}\n" -o /dev/null diff --git a/samples/12-cleanup.sh b/samples/12-cleanup.sh new file mode 100755 index 0000000..d62362a --- /dev/null +++ b/samples/12-cleanup.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +set -Eeuo pipefail + +. 3scale-admin-cli.sh + +if [ -z "${THREESCALE_TOKEN:-}" ]; then + echo "Please set the THREESCALE_TOKEN environment variable!" + exit 1 +fi + +if [ -z "${ADMIN_PORTAL_HOSTNAME:-}" ]; then + echo "Please set the ADMIN_PORTAL_HOSTNAME environment variable!" + exit 1 +fi + +if [ -z "${SERVICE_NAME:-}" ]; then + echo "Please set the SERVICE_NAME environment variable!" + exit 1 +fi + +## +## Service deletion +## +declare -A service_def=( ["system_name"]="${SERVICE_NAME}" ) +apply service absent service_def + +echo "Waiting 5 seconds before deleting the backend..." +sleep 5 + +## +## Backend deletion +## +declare -A backend_def=( ["system_name"]="${SERVICE_NAME}" ) +apply backend absent backend_def diff --git a/src/00-factory.sh b/src/00-factory.sh new file mode 100644 index 0000000..710e986 --- /dev/null +++ b/src/00-factory.sh @@ -0,0 +1,43 @@ +declare -A threescale_object_types=( ["backend"]="backend_apis" ["service"]="services" ["metric"]="metrics" ["method"]="methods" ["mapping_rule"]="mapping_rules" ["backend_usage"]="backend_usages" ["application_plan"]="application_plans" ["application_apikey"]="applications" ["application_appid"]="applications" ) +declare -A threescale_contains_external_id=( ["metric"]="contains_external_id_with_prefix" ["method"]="contains_external_id_with_prefix" ["backend_usage"]="contains_numerical_external_id") +declare -A threescale_id_of_external_id=( ["metric"]="id_of_external_id_with_prefix" ["method"]="id_of_external_id_with_prefix" ["backend_usage"]="id_of_numerical_external_id") +declare -A threescale_external_id=( ["backend_usage"]="backend_id" ["application_apikey"]="user_key" ["application_appid"]="application_id" ) + +template=$(cat <<"EOF" +function OBJECT_TYPE_list () { + echo "Finding all OBJECT_TYPE..." >&2 + threescale_call OBJECT_TYPE_list GET "$(breadcrumb_to_path breadcrumb)/URL_PART.json" "200" + if [ $? -gt 0 ]; then + return $? + fi + + get_result OBJECT_TYPE_list | cleanup_list +} + +function OBJECT_TYPE_create () { + local -n OBJECT_TYPE=$1 + local stable_id="$(external_id_factory OBJECT_TYPE)" + echo "Creating OBJECT_TYPE with $stable_id ${OBJECT_TYPE[$stable_id]:-}..." >&2 + threescale_call OBJECT_TYPE_create POST "$(breadcrumb_to_path breadcrumb)/URL_PART.json" "201" $(map_to_curl OBJECT_TYPE) +} + +function OBJECT_TYPE_update () { + local id=$1 + local -n OBJECT_TYPE=$2 + local stable_id="$(external_id_factory OBJECT_TYPE)" + echo "Updating OBJECT_TYPE with $stable_id ${OBJECT_TYPE[$stable_id]:-} and id $id..." >&2 + threescale_call OBJECT_TYPE_update PUT "$(breadcrumb_to_path breadcrumb)/URL_PART/$id.json" "200" $(map_to_curl OBJECT_TYPE) +} + +function OBJECT_TYPE_delete () { + local id=$1 + echo "Deleting OBJECT_TYPE with id $id..." >&2 + threescale_call OBJECT_TYPE_delete DELETE "$(breadcrumb_to_path breadcrumb)/URL_PART/$id.json" "200" +} +EOF +) + +for object_type in "${!threescale_object_types[@]}"; do + url_part="${threescale_object_types[$object_type]}" + eval "$(echo "$template" | sed "s/OBJECT_TYPE/$object_type/g; s/URL_PART/$url_part/g")" +done diff --git a/src/01-common.sh b/src/01-common.sh new file mode 100644 index 0000000..f894668 --- /dev/null +++ b/src/01-common.sh @@ -0,0 +1,183 @@ +declare tmp=$(mktemp -d -t threescale-XXXXXXXXXX) + +function threescale_call () { + request_name="$1" + method="$2" + service="$3" + expected_http_codes="$4" + shift 4 + + # Make HTTP methods uppercase + method="${method^^}" + + local -a curl_args=( -sk --write-out "%{http_code}" -o "$tmp/$request_name.json" -X "$method" "$@") + local url="https://$ADMIN_PORTAL_HOSTNAME/admin/api$service" + + if [[ "$method" == "GET" || "$method" == "DELETE" ]]; then + if [[ "$url" =~ .*\?.* ]]; then + url="$url&access_token=$THREESCALE_TOKEN" + else + url="$url?access_token=$THREESCALE_TOKEN" + fi + else + curl_args+=( "-d" "access_token=$THREESCALE_TOKEN" ) + fi + + curl_args+=( "$url" ) + + if [ "${CURL_DEBUG:-}" != "" ]; then + echo curl "${curl_args[@]}" ">" "$tmp/$request_name.code" >&2 + fi + + curl "${curl_args[@]}" > "$tmp/$request_name.code" + ret=$? + if [ $ret -gt 0 ]; then + echo "curl exited with rc = $ret" >&2 + return $ret + fi + + if [ "${CURL_DEBUG:-}" != "" ]; then + echo "=> HTTP $(cat "$tmp/$request_name.code")" >&2 + fi + + if ! egrep -q "^$expected_http_codes\$" "$tmp/$request_name.code"; then + echo "Unexpected HTTP code: $(cat "$tmp/$request_name.code")" >&2 + return 1 + fi + + return 0 +} + +function get_result_file () { + request_name="$1" + echo -n "$tmp/$request_name.json" +} + +function get_result () { + request_name="$1" + cat "$tmp/$request_name.json" +} + +function cleanup_list () { + jq 'to_entries | .[0].value | map(to_entries | .[0].value)' +} + +function cleanup_item () { + jq 'to_entries | .[0].value' +} + +function map_to_curl () { + local -n map=$1 + for k in "${!map[@]}"; do + v="$(urlencode "${map[$k]}")" + echo -d "$k=$v" + done +} + +declare -a breadcrumb + +function breadcrumb_to_path () { + join_by "/" "" ${breadcrumb[@]} +} + +declare last_object_id + +function apply () { + local object_type="$1" + local state="$2" + local -n object=$3 + + ${object_type}_list > "$tmp/apply_list.json" + ret=$? + if [ $ret -gt 0 ]; then + echo "${object_type}_list exited with rc = $ret" >&2 + return $ret + fi + local list="$(cat $tmp/apply_list.json)" + + external_id="$(external_id_factory $object_type)" + + last_object_id= + case "$state" in + "present") + if echo "$list" | "$(contains_external_id_factory "$object_type")" "$external_id" "${object[$external_id]}"; then + local id="$(echo "$list" | "$(id_of_external_id_factory "$object_type")" "$external_id" "${object[$external_id]}")" + "${object_type}_update" "$id" object + ret=$? + if [ $ret -gt 0 ]; then + echo "${object_type}_update exited with rc = $ret" >&2 + return $ret + fi + last_object_id="$id" + else + "${object_type}_create" object + ret=$? + if [ $ret -gt 0 ]; then + echo "${object_type}_create exited with rc = $ret" >&2 + return $ret + fi + last_object_id="$(get_result "${object_type}_create" | cleanup_item | jq -r .id)" + fi + ;; + "absent") + if echo "$list" | "$(contains_external_id_factory "$object_type")" "$external_id" "${object[$external_id]}"; then + local id="$(echo "$list" | "$(id_of_external_id_factory "$object_type")" "$external_id" "${object[$external_id]}" )" + "${object_type}_delete" "$id" + ret=$? + if [ $ret -gt 0 ]; then + echo "${object_type}_delete exited with rc = $ret" >&2 + return $ret + fi + fi + ;; + esac +} + +function delete_all () { + local object_type="$1" + "${object_type}_list" | jq -r '.[] | .id' | while read id; do + "${object_type}_delete" "$id" + done +} + +function external_id_factory () { + local object_type="$1" + id="${threescale_external_id[$object_type]:-}" + echo "${id:-system_name}" +} + +function contains_external_id_factory () { + local object_type="$1" + fn="${threescale_contains_external_id[$object_type]:-}" + echo "${fn:-contains_external_id}" +} + +function id_of_external_id_factory () { + local object_type="$1" + fn="${threescale_id_of_external_id[$object_type]:-}" + echo "${fn:-id_of_external_id}" +} + +function contains_external_id_with_prefix () { + jq --arg k "$1" --arg v "$2" -e '[ .[] | select(.[$k] | startswith($v + ".")) ] | length != 0' > /dev/null +} + +function id_of_external_id_with_prefix () { + jq --arg k "$1" --arg v "$2" -r '.[] | select(.[$k] | startswith($v + ".")) | .id ' +} + +function contains_external_id () { + jq --arg k "$1" --arg v "$2" -e '[ .[] | select(.[$k] == $v) ] | length != 0' > /dev/null +} + +function id_of_external_id () { + jq --arg k "$1" --arg v "$2" -r '.[] | select(.[$k] == $v) | .id ' +} + +function contains_numerical_external_id () { + jq --arg k "$1" --arg v "$2" -e '[ .[] | select(.[$k] == ($v|tonumber)) ] | length != 0' > /dev/null +} + +function id_of_numerical_external_id () { + jq --arg k "$1" --arg v "$2" -r '.[] | select(.[$k] == ($v|tonumber)) | .id ' +} diff --git a/src/02-util.sh b/src/02-util.sh new file mode 100644 index 0000000..32a0949 --- /dev/null +++ b/src/02-util.sh @@ -0,0 +1,27 @@ +# https://gist.github.com/cdown/1163649 + +export LC_COLLATE=C + +urlencode() { + # urlencode + + local length="${#1}" + for (( i = 0; i < length; i++ )); do + local c="${1:$i:1}" + case $c in + [a-zA-Z0-9.~_-]) printf '%s' "$c" ;; + *) printf '%%%02X' "'$c" ;; + esac + done +} + +urldecode() { + # urldecode + + local url_encoded="${1//+/ }" + printf '%b' "${url_encoded//%/\\x}" +} + +# https://stackoverflow.com/questions/1527049/how-can-i-join-elements-of-an-array-in-bash +function join_by { local IFS="$1"; shift; echo "$*"; } + diff --git a/src/20-account.sh b/src/20-account.sh new file mode 100644 index 0000000..620b6f5 --- /dev/null +++ b/src/20-account.sh @@ -0,0 +1,9 @@ +function find_first_account () { + echo "Finding the default first account..." >&2 + threescale_call find_first_account GET "/accounts.json?state=approved&page=1&per_page=1" "200" + if [ $? -gt 0 ]; then + return $? + fi + + get_result find_first_account | cleanup_list | jq '.[0]' +} diff --git a/src/22-backend_usage.sh b/src/22-backend_usage.sh new file mode 100644 index 0000000..4575c35 --- /dev/null +++ b/src/22-backend_usage.sh @@ -0,0 +1,25 @@ +function backend_usage_list () { + echo "Finding all backend_usage..." >&2 + threescale_call backend_usage_list GET "$(breadcrumb_to_path breadcrumb)/backend_usages.json" "200" + if [ $? -gt 0 ]; then + return $? + fi + + get_result backend_usage_list | jq 'map(to_entries |.[0].value)' +} + +function backend_usage_create () { + local -n backend_usage=$1 + + local -A api_call_payload + for key in "${!backend_usage[@]}"; do + if [ "$key" == "backend_id" ]; then + api_call_payload[backend_api_id]="${backend_usage[backend_id]}" + else + api_call_payload[$key]="${backend_usage[$key]}" + fi + done + + echo "Creating backend_usage with backend_id = ${api_call_payload[backend_api_id]:-}..." >&2 + threescale_call backend_usage_create POST "$(breadcrumb_to_path breadcrumb)/backend_usages.json" "201" $(map_to_curl api_call_payload) +} diff --git a/src/23-policy.sh b/src/23-policy.sh new file mode 100644 index 0000000..c8581b7 --- /dev/null +++ b/src/23-policy.sh @@ -0,0 +1,26 @@ +function policy_list () { + echo "Finding all policies..." >&2 + threescale_call policies_list GET "$(breadcrumb_to_path breadcrumb)/proxy/policies.json" "200" + if [ $? -gt 0 ]; then + return $? + fi + + get_result policies_list | cleanup_item +} + +function policy_update () { + local policy_chain="$1" + + echo "Updating policies..." >&2 + threescale_call policy_update PUT "$(breadcrumb_to_path breadcrumb)/proxy/policies.json" "200" --data-urlencode "policies_config=$1" +} + +function contains_policy () { + policy_name="$1" + jq --arg policy_name "$policy_name" -e '[ .[] | select(.name == $policy_name) ] | length != 0' > /dev/null +} + +function remove_policy () { + policy_name="$1" + jq --arg policy_name "$policy_name" -r '[ .[] | select(.name != $policy_name) ]' +} diff --git a/src/24-proxy.sh b/src/24-proxy.sh new file mode 100644 index 0000000..3810adc --- /dev/null +++ b/src/24-proxy.sh @@ -0,0 +1,25 @@ +function proxy_update () { + local -n proxy=$1 + echo "Updating proxy..." >&2 + threescale_call proxy_update PUT "$(breadcrumb_to_path breadcrumb)/proxy.json" "200" $(map_to_curl proxy) +} + +function proxy_version () { + local environment=$1 + + echo "Getting proxy version of env $environment..." >&2 + threescale_call proxy_version GET "$(breadcrumb_to_path breadcrumb)/proxy/configs/${environment}/latest.json" "200" + if [ $? -gt 0 ]; then + return $? + fi + + get_result proxy_version | cleanup_item | jq '.version' +} + +function proxy_promote () { + local environment="$1" + local version="$2" + echo "Promoting proxy version $version of env $environment..." >&2 + threescale_call proxy_promote POST "$(breadcrumb_to_path breadcrumb)/proxy/configs/${environment}/${version}/promote.json" "201" -d to=production +} + diff --git a/src/25-oidc-config.sh b/src/25-oidc-config.sh new file mode 100644 index 0000000..8c0cb24 --- /dev/null +++ b/src/25-oidc-config.sh @@ -0,0 +1,6 @@ +function oidc_configuration_update () { + local -n oidc_configuration=$1 + + echo "Updating OIDC configuration..." >&2 + threescale_call policy_update PATCH "$(breadcrumb_to_path breadcrumb)/proxy/oidc_configuration.json" "200" $(map_to_curl oidc_configuration) +}