Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ container variable below is passed through if set.
| `toolbox-login --begin` / `--wait` | The same login in two halves: print the URL and return (idempotent), then wait for approval — about 90 s per call, exit 2 means call again. For callers that can't sit on a blocking command. |
| `toolbox-login --force` | Re-authenticate, e.g. to switch accounts. |
| `toolbox-token` | Print the raw token, for scripting. |
| `promtool …` | Prometheus CLI, pointed at Thanos. Metrics, plus alert state and rule listings. |
| `promtool query instant\|range …` | Prometheus CLI, pointed at Thanos. Metrics, plus alert state. The server argument is filled in for you. `query series`/`labels` and `debug` take no `--header`, so they cannot reach the cluster. |
| `logcli …` | Loki CLI. Log queries and `--tail`. |
| `tempo-cli query api …` | Tempo CLI. TraceQL search and trace lookup. |
| `grafana-ds <type>` | Print a datasource UID (`prometheus`/`loki`/`tempo`); used by the wrappers. |
Expand Down Expand Up @@ -196,6 +196,14 @@ directly, so there is one edge host and one credential for everything. The
wrappers resolve the datasource UID at run time (`grafana-ds`), since Grafana
generates UIDs per cluster.

`promtool` differs in one way the wrapper hides: only `query instant` and
`query range` accept `--header`. Those two get the `<server>` positional filled in
with the proxy URL. `query series`, `query labels` and every `debug` subcommand
take no `--header`, so a request from them reaches the edge with no credential and
is redirected to the login page; the wrapper refuses those against this cluster
rather than letting them fail as a confusing 302, and passes an explicit
`http(s)` server through untouched.

`tempo-cli` differs in three ways the wrapper hides: headers are `Key=Value` not
`Key: Value`; `search` takes a bare host plus `--path-prefix` while `trace-id`
takes a full URL; and `--use-grpc` is refused, because headers would then travel
Expand Down
71 changes: 55 additions & 16 deletions bin/promtool
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,59 @@
# oauth2-proxy at the edge, X-JWT-Assertion by Grafana's [auth.jwt]. Sending only
# one gets a 302 to the login page.
#
# `query`/`debug` subcommands take a <server> argument; everything else (check,
# push, test) is local and passed straight through.
# Only `query instant` and `query range` accept --header, so they are the only
# subcommands that can carry a credential and therefore the only ones that can
# reach this cluster. For them the wrapper also resolves the datasource UID and
# inserts the <server> positional, so callers never build the proxy URL by hand --
# the same job logcli's --addr and tempo-cli's --path-prefix do.
#
# `query series`, `query labels` and every `debug` subcommand take no --header:
# their requests would arrive at the edge with no credential and be redirected to
# the login page, so aiming them at this cluster is refused rather than left to
# fail as a confusing 302. An explicit http(s) server is always passed through
# untouched, for promtool against something else.
#
# Everything else (check, push, test, tsdb) is local and passed straight through.
set -euo pipefail
case "${1:-}" in
query|debug)
if ! TOKEN="$(toolbox-token --no-login)"; then
echo "toolbox: not authenticated. Run 'toolbox-login' first." >&2; exit 1
fi
sub="$1"; shift
exec /usr/local/bin/promtool.real "$sub" "$@" \
--header "Authorization: Bearer ${TOKEN}" \
--header "X-JWT-Assertion: ${TOKEN}"
;;
*)
exec /usr/local/bin/promtool.real "$@"
;;
esac

# Is any argument already a server URL? An explicit one always wins.
has_server() {
local a
for a in "$@"; do
case "$a" in http://*|https://*) return 0 ;; esac
done
return 1
}

if [ "${1:-}" = "query" ]; then
sub="${2:-}"
# No subcommand, or asking for help: there is nothing to point at a server.
case "$sub" in ""|-*) exec /usr/local/bin/promtool.real "$@" ;; esac
shift 2
case "$sub" in
instant|range)
if ! TOKEN="$(toolbox-token --no-login)"; then
echo "toolbox: not authenticated. Run 'toolbox-login' first." >&2; exit 1
fi
hdrs=(--header "Authorization: Bearer ${TOKEN}" --header "X-JWT-Assertion: ${TOKEN}")
if has_server "$@"; then
exec /usr/local/bin/promtool.real query "$sub" "$@" "${hdrs[@]}"
fi
server="https://grafana.${TOOLBOX_CAPTAIN_DOMAIN}/api/datasources/proxy/uid/$(grafana-ds prometheus)"
exec /usr/local/bin/promtool.real query "$sub" "$server" "$@" "${hdrs[@]}"
;;
*)
has_server "$@" && exec /usr/local/bin/promtool.real query "$sub" "$@"
echo "toolbox: 'promtool query $sub' takes no --header, so it cannot authenticate to this cluster. Use 'query instant' or 'query range' (labels are also available as 'logcli labels'), or pass an explicit server." >&2
exit 1
;;
esac
fi

if [ "${1:-}" = "debug" ]; then
has_server "${@:2}" && exec /usr/local/bin/promtool.real "$@"
echo "toolbox: 'promtool debug' takes no --header, so it cannot authenticate to this cluster. Pass an explicit server to use it against something else." >&2
exit 1
fi

exec /usr/local/bin/promtool.real "$@"
Loading