This is an automated email from the ASF dual-hosted git repository. wu-sheng pushed a commit to branch e2e/swctl-admin-migration in repository https://gitbox.apache.org/repos/asf/skywalking.git
commit d2bf1a311f0e300e8e62a43b13f4263605058727 Author: Wu Sheng <[email protected]> AuthorDate: Wed Jun 3 21:50:43 2026 +0800 Migrate e2e admin-API curl calls to swctl admin commands Replace raw curl interactions with the OAP admin-server REST host (port 17128) across the e2e suite with the new 'swctl admin ...' command tree (skywalking-cli #228, commit b447211): - runtime-rule flows (mal/lal/cluster) -> swctl admin runtime-rule ... - dsl-debug flows (oal/mal/lal-block/lal-statement) -> swctl admin dsl-debug ... - ui-management -> swctl admin ui-template ... - storage inspect/config(dump,ttl) -> swctl admin inspect|config ... --display json/yaml keeps response bodies parseable by the existing jq/yq assertions; runtime-rule negative paths assert the CLI's typed error envelope (HTTP <code> (<applyStatus>)). config-dump.yml is regenerated as the JSON string-map that 'admin config dump' returns. Bumps SW_CTL_COMMIT to the admin-capable commit. Non-admin curls (BanyanDB probe, self-telemetry, alarm webhook) are intentionally kept. --- .../dsl-debugging/lal-block/dsl-debug-flow.sh | 30 +- test/e2e-v2/cases/dsl-debugging/lal-block/e2e.yaml | 2 +- .../dsl-debugging/lal-statement/dsl-debug-flow.sh | 30 +- .../cases/dsl-debugging/lal-statement/e2e.yaml | 2 +- .../cases/dsl-debugging/mal/dsl-debug-flow.sh | 29 +- test/e2e-v2/cases/dsl-debugging/mal/e2e.yaml | 2 +- .../cases/dsl-debugging/oal/dsl-debug-flow.sh | 19 +- .../cases/runtime-rule/cluster/cluster-flow.sh | 25 +- test/e2e-v2/cases/runtime-rule/cluster/e2e.yaml | 2 +- test/e2e-v2/cases/runtime-rule/lal/e2e.yaml | 2 +- test/e2e-v2/cases/runtime-rule/lal/lal-flow.sh | 27 +- .../runtime-rule/mal-storage/banyandb/e2e.yaml | 2 +- .../mal-storage/elasticsearch/e2e.yaml | 2 +- .../runtime-rule/mal-storage/postgresql/e2e.yaml | 2 +- .../runtime-rule/mal-storage/runtime-rule-flow.sh | 180 +++++---- test/e2e-v2/cases/storage/banyandb/e2e.yaml | 2 +- test/e2e-v2/cases/storage/es/e2e.yaml | 2 +- test/e2e-v2/cases/storage/es/es-sharding/e2e.yaml | 2 +- test/e2e-v2/cases/storage/expected/config-dump.yml | 428 +++++++++++---------- test/e2e-v2/cases/storage/mysql/e2e.yaml | 5 +- test/e2e-v2/cases/storage/opensearch/e2e.yaml | 2 +- test/e2e-v2/cases/storage/postgres/e2e.yaml | 2 +- test/e2e-v2/cases/storage/storage-cases.yaml | 35 +- .../cases/ui-management/ui-management-cases.yaml | 69 ++-- test/e2e-v2/script/env | 2 +- 25 files changed, 470 insertions(+), 435 deletions(-) diff --git a/test/e2e-v2/cases/dsl-debugging/lal-block/dsl-debug-flow.sh b/test/e2e-v2/cases/dsl-debugging/lal-block/dsl-debug-flow.sh index b22cd0fbfc..ceeff95a2b 100755 --- a/test/e2e-v2/cases/dsl-debugging/lal-block/dsl-debug-flow.sh +++ b/test/e2e-v2/cases/dsl-debugging/lal-block/dsl-debug-flow.sh @@ -39,6 +39,11 @@ OAP_HOST="${OAP_HOST:-127.0.0.1}" OAP_REST_PORT="${OAP_REST_PORT:-17128}" OAP_BASE="http://${OAP_HOST}:${OAP_REST_PORT}" +# All admin-server REST calls go through swctl's `admin` command tree instead of +# raw curl. `--display json` keeps the body shape identical to the old curl +# output, so the downstream jq assertions are unchanged. +admin() { swctl --display json --admin-url="${OAP_BASE}" admin "$@"; } + SETTLE_SECONDS="${SETTLE_SECONDS:-180}" # Reuse the same multi-statement seed the statement case uses, so comparing @@ -57,27 +62,25 @@ CLIENT_ID="e2e-dsldbg-lal-block-1" log "waiting for OAP admin port" deadline=$(( $(date +%s) + 90 )) -until curl -fsS "${OAP_BASE}/dsl-debugging/status" >/dev/null 2>&1; do +until admin dsl-debug status >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP admin not ready after 90s"; fi sleep 2 done # --- Phase 0: status check ------------------------------------------------------------ -log "=== Phase 0: /dsl-debugging/status ===" -status_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/status")" +log "=== Phase 0: dsl-debug status ===" +status_body="$(admin dsl-debug status)" echo "${status_body}" | jq -e '.injectionEnabled == true' >/dev/null \ || fail "injectionEnabled is not true" # --- Phase 1: apply runtime-rule LAL -------------------------------------------------- log "=== Phase 1: apply runtime-rule LAL seed (shared with lal-statement case) ===" -curl -fsS -XPOST -H 'Content-Type: text/plain' \ - --data-binary "@${SEED_LAL}" \ - "${OAP_BASE}/runtime/rule/addOrUpdate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null \ +admin runtime-rule add --catalog "${RR_CATALOG}" --name "${RR_NAME}" -f "${SEED_LAL}" >/dev/null \ || fail "addOrUpdate ${RR_CATALOG}/${RR_NAME} failed" deadline=$(( $(date +%s) + 60 )) status="" while (( $(date +%s) < deadline )); do - status="$(curl -fsS "${OAP_BASE}/runtime/rule/list" 2>/dev/null \ + status="$(admin runtime-rule list 2>/dev/null \ | jq -r --arg c "${RR_CATALOG}" --arg n "${RR_NAME}" \ '.rules[] | select(.catalog == $c and .name == $n) | .status' | head -1)" [ "${status}" = "ACTIVE" ] && break @@ -88,8 +91,9 @@ log "✓ seed applied: ${RR_CATALOG}/${RR_NAME} ACTIVE" # --- Phase 2: install with granularity=block (default) -------------------------------- log "=== Phase 2: install session with granularity=block ===" -install_body="$(curl -fsS -XPOST \ - "${OAP_BASE}/dsl-debugging/session?catalog=${DBG_CATALOG}&name=${DBG_NAME}&ruleName=${DBG_RULE_NAME}&clientId=${CLIENT_ID}&granularity=block")" +install_body="$(admin dsl-debug session start \ + --catalog "${DBG_CATALOG}" --name "${DBG_NAME}" --rule-name "${DBG_RULE_NAME}" \ + --client-id "${CLIENT_ID}" --granularity block)" log " install → ${install_body}" SESSION_ID="$(echo "${install_body}" | jq -r '.sessionId // empty')" [ -n "${SESSION_ID}" ] || fail "install did not return sessionId" @@ -103,7 +107,7 @@ deadline=$(( $(date +%s) + SETTLE_SECONDS )) records_count=0 collect_body="" while (( $(date +%s) < deadline )); do - collect_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}")" + collect_body="$(admin dsl-debug session get "${SESSION_ID}")" records_count="$(echo "${collect_body}" | jq '[.nodes[].records[]] | length')" if [ "${records_count}" -gt 0 ]; then # Block mode: a fully walked record has at minimum input + output samples. @@ -160,13 +164,13 @@ log "✓ block-mode shape valid (${records_count} records; per-statement probes # --- Phase 6: stop session ------------------------------------------------------------ log "=== Phase 6: stop session ===" -stop_body="$(curl -fsS -XPOST "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}/stop")" +stop_body="$(admin dsl-debug session stop "${SESSION_ID}")" echo "${stop_body}" | jq -e '.localStopped == true' >/dev/null \ || fail "localStopped != true" # --- Phase 7: cleanup runtime-rule ---------------------------------------------------- log "=== Phase 7: cleanup runtime-rule ===" -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/inactivate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/delete?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true +admin runtime-rule inactivate --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true +admin runtime-rule delete --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true log "=== ALL LAL BLOCK FLOW PHASES PASSED ===" diff --git a/test/e2e-v2/cases/dsl-debugging/lal-block/e2e.yaml b/test/e2e-v2/cases/dsl-debugging/lal-block/e2e.yaml index c7afbc6009..7d655867d1 100644 --- a/test/e2e-v2/cases/dsl-debugging/lal-block/e2e.yaml +++ b/test/e2e-v2/cases/dsl-debugging/lal-block/e2e.yaml @@ -52,7 +52,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/dsl-debugging/status >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin dsl-debug status >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/dsl-debugging/lal-statement/dsl-debug-flow.sh b/test/e2e-v2/cases/dsl-debugging/lal-statement/dsl-debug-flow.sh index 6ab7dcaea2..ff24bd6e1a 100755 --- a/test/e2e-v2/cases/dsl-debugging/lal-statement/dsl-debug-flow.sh +++ b/test/e2e-v2/cases/dsl-debugging/lal-statement/dsl-debug-flow.sh @@ -35,6 +35,11 @@ OAP_HOST="${OAP_HOST:-127.0.0.1}" OAP_REST_PORT="${OAP_REST_PORT:-17128}" OAP_BASE="http://${OAP_HOST}:${OAP_REST_PORT}" +# All admin-server REST calls go through swctl's `admin` command tree instead of +# raw curl. `--display json` keeps the body shape identical to the old curl +# output, so the downstream jq assertions are unchanged. +admin() { swctl --display json --admin-url="${OAP_BASE}" admin "$@"; } + SETTLE_SECONDS="${SETTLE_SECONDS:-180}" SEED_DIR="${SEED_DIR:-$(pwd)/test/e2e-v2/cases/dsl-debugging/lal-statement/seed-rules}" @@ -50,27 +55,25 @@ CLIENT_ID="e2e-dsldbg-lal-stmt-1" log "waiting for OAP admin port" deadline=$(( $(date +%s) + 90 )) -until curl -fsS "${OAP_BASE}/dsl-debugging/status" >/dev/null 2>&1; do +until admin dsl-debug status >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP admin not ready after 90s"; fi sleep 2 done # --- Phase 0: status check ------------------------------------------------------------ -log "=== Phase 0: /dsl-debugging/status ===" -status_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/status")" +log "=== Phase 0: dsl-debug status ===" +status_body="$(admin dsl-debug status)" echo "${status_body}" | jq -e '.injectionEnabled == true' >/dev/null \ || fail "injectionEnabled is not true" # --- Phase 1: apply runtime-rule LAL -------------------------------------------------- log "=== Phase 1: apply runtime-rule LAL seed ===" -curl -fsS -XPOST -H 'Content-Type: text/plain' \ - --data-binary "@${SEED_LAL}" \ - "${OAP_BASE}/runtime/rule/addOrUpdate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null \ +admin runtime-rule add --catalog "${RR_CATALOG}" --name "${RR_NAME}" -f "${SEED_LAL}" >/dev/null \ || fail "addOrUpdate ${RR_CATALOG}/${RR_NAME} failed" deadline=$(( $(date +%s) + 60 )) status="" while (( $(date +%s) < deadline )); do - status="$(curl -fsS "${OAP_BASE}/runtime/rule/list" 2>/dev/null \ + status="$(admin runtime-rule list 2>/dev/null \ | jq -r --arg c "${RR_CATALOG}" --arg n "${RR_NAME}" \ '.rules[] | select(.catalog == $c and .name == $n) | .status' | head -1)" [ "${status}" = "ACTIVE" ] && break @@ -81,8 +84,9 @@ log "✓ seed applied: ${RR_CATALOG}/${RR_NAME} ACTIVE" # --- Phase 2: install with granularity=statement -------------------------------------- log "=== Phase 2: install session with granularity=statement ===" -install_body="$(curl -fsS -XPOST \ - "${OAP_BASE}/dsl-debugging/session?catalog=${DBG_CATALOG}&name=${DBG_NAME}&ruleName=${DBG_RULE_NAME}&clientId=${CLIENT_ID}&granularity=statement")" +install_body="$(admin dsl-debug session start \ + --catalog "${DBG_CATALOG}" --name "${DBG_NAME}" --rule-name "${DBG_RULE_NAME}" \ + --client-id "${CLIENT_ID}" --granularity statement)" log " install → ${install_body}" SESSION_ID="$(echo "${install_body}" | jq -r '.sessionId // empty')" [ -n "${SESSION_ID}" ] || fail "install did not return sessionId" @@ -96,7 +100,7 @@ deadline=$(( $(date +%s) + SETTLE_SECONDS )) records_count=0 collect_body="" while (( $(date +%s) < deadline )); do - collect_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}")" + collect_body="$(admin dsl-debug session get "${SESSION_ID}")" records_count="$(echo "${collect_body}" | jq '[.nodes[].records[]] | length')" if [ "${records_count}" -gt 0 ]; then # Wait for a record with several samples — statement mode produces one @@ -162,13 +166,13 @@ log "✓ statement-mode shape valid (${records_count} records, ${distinct} disti # --- Phase 6: stop session ------------------------------------------------------------ log "=== Phase 6: stop session ===" -stop_body="$(curl -fsS -XPOST "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}/stop")" +stop_body="$(admin dsl-debug session stop "${SESSION_ID}")" echo "${stop_body}" | jq -e '.localStopped == true' >/dev/null \ || fail "localStopped != true" # --- Phase 7: cleanup runtime-rule ---------------------------------------------------- log "=== Phase 7: cleanup runtime-rule ===" -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/inactivate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/delete?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true +admin runtime-rule inactivate --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true +admin runtime-rule delete --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true log "=== ALL LAL STATEMENT FLOW PHASES PASSED ===" diff --git a/test/e2e-v2/cases/dsl-debugging/lal-statement/e2e.yaml b/test/e2e-v2/cases/dsl-debugging/lal-statement/e2e.yaml index 23fa7b7700..ee84a35c7d 100644 --- a/test/e2e-v2/cases/dsl-debugging/lal-statement/e2e.yaml +++ b/test/e2e-v2/cases/dsl-debugging/lal-statement/e2e.yaml @@ -51,7 +51,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/dsl-debugging/status >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin dsl-debug status >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/dsl-debugging/mal/dsl-debug-flow.sh b/test/e2e-v2/cases/dsl-debugging/mal/dsl-debug-flow.sh index 267234d760..7b2b051ebb 100755 --- a/test/e2e-v2/cases/dsl-debugging/mal/dsl-debug-flow.sh +++ b/test/e2e-v2/cases/dsl-debugging/mal/dsl-debug-flow.sh @@ -37,6 +37,11 @@ OAP_HOST="${OAP_HOST:-127.0.0.1}" OAP_REST_PORT="${OAP_REST_PORT:-17128}" OAP_BASE="http://${OAP_HOST}:${OAP_REST_PORT}" +# All admin-server REST calls go through swctl's `admin` command tree instead of +# raw curl. `--display json` keeps the body shape identical to the old curl +# output, so the downstream jq assertions are unchanged. +admin() { swctl --display json --admin-url="${OAP_BASE}" admin "$@"; } + SETTLE_SECONDS="${SETTLE_SECONDS:-300}" SEED_DIR="${SEED_DIR:-$(pwd)/test/e2e-v2/cases/dsl-debugging/mal/seed-rules}" @@ -53,26 +58,24 @@ CLIENT_ID="e2e-dsldbg-mal-1" log "waiting for OAP admin port" deadline=$(( $(date +%s) + 120 )) -until curl -fsS "${OAP_BASE}/dsl-debugging/status" >/dev/null 2>&1; do +until admin dsl-debug status >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP admin not ready after 120s"; fi sleep 2 done # --- Phase 0: status ------------------------------------------------------------------ -log "=== Phase 0: /dsl-debugging/status ===" -curl -fsS "${OAP_BASE}/dsl-debugging/status" | jq -e '.injectionEnabled == true' >/dev/null \ +log "=== Phase 0: dsl-debug status ===" +admin dsl-debug status | jq -e '.injectionEnabled == true' >/dev/null \ || fail "injectionEnabled is not true" # --- Phase 1: apply runtime-rule MAL -------------------------------------------------- log "=== Phase 1: apply runtime-rule MAL seed ===" -curl -fsS -XPOST -H 'Content-Type: text/plain' \ - --data-binary "@${SEED_MAL}" \ - "${OAP_BASE}/runtime/rule/addOrUpdate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null \ +admin runtime-rule add --catalog "${RR_CATALOG}" --name "${RR_NAME}" -f "${SEED_MAL}" >/dev/null \ || fail "addOrUpdate ${RR_CATALOG}/${RR_NAME} failed" deadline=$(( $(date +%s) + 60 )) status="" while (( $(date +%s) < deadline )); do - status="$(curl -fsS "${OAP_BASE}/runtime/rule/list" 2>/dev/null \ + status="$(admin runtime-rule list 2>/dev/null \ | jq -r --arg c "${RR_CATALOG}" --arg n "${RR_NAME}" \ '.rules[] | select(.catalog == $c and .name == $n) | .status' | head -1)" [ "${status}" = "ACTIVE" ] && break @@ -83,8 +86,8 @@ log "✓ seed applied: ${RR_CATALOG}/${RR_NAME} ACTIVE" # --- Phase 2: install session --------------------------------------------------------- log "=== Phase 2: install session on (${DBG_CATALOG}, ${DBG_NAME}, ${DBG_RULE_NAME}) ===" -install_body="$(curl -fsS -XPOST \ - "${OAP_BASE}/dsl-debugging/session?catalog=${DBG_CATALOG}&name=${DBG_NAME}&ruleName=${DBG_RULE_NAME}&clientId=${CLIENT_ID}")" +install_body="$(admin dsl-debug session start \ + --catalog "${DBG_CATALOG}" --name "${DBG_NAME}" --rule-name "${DBG_RULE_NAME}" --client-id "${CLIENT_ID}")" log " install → ${install_body}" SESSION_ID="$(echo "${install_body}" | jq -r '.sessionId // empty')" [ -n "${SESSION_ID}" ] || fail "install did not return sessionId — body: ${install_body}" @@ -96,7 +99,7 @@ deadline=$(( $(date +%s) + SETTLE_SECONDS )) records_count=0 collect_body="" while (( $(date +%s) < deadline )); do - collect_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}")" + collect_body="$(admin dsl-debug session get "${SESSION_ID}")" records_count="$(echo "${collect_body}" | jq '[.nodes[].records[]] | length')" if [ "${records_count}" -gt 0 ]; then # Wait for at least one full execution (terminal meterEmit closed it), @@ -218,13 +221,13 @@ log "✓ MAL shape valid (${records_count} records, ${total_samples} samples)" # --- Phase 6: stop session ------------------------------------------------------------ log "=== Phase 6: stop session ===" -curl -fsS -XPOST "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}/stop" \ +admin dsl-debug session stop "${SESSION_ID}" \ | jq -e '.localStopped == true' >/dev/null \ || fail "localStopped != true" # --- Phase 7: cleanup runtime-rule ---------------------------------------------------- log "=== Phase 7: cleanup runtime-rule ===" -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/inactivate?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true -curl -fsS -XPOST "${OAP_BASE}/runtime/rule/delete?catalog=${RR_CATALOG}&name=${RR_NAME}" >/dev/null || true +admin runtime-rule inactivate --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true +admin runtime-rule delete --catalog "${RR_CATALOG}" --name "${RR_NAME}" >/dev/null || true log "=== ALL MAL FLOW PHASES PASSED ===" diff --git a/test/e2e-v2/cases/dsl-debugging/mal/e2e.yaml b/test/e2e-v2/cases/dsl-debugging/mal/e2e.yaml index c0fe353257..d2c9dec2d4 100644 --- a/test/e2e-v2/cases/dsl-debugging/mal/e2e.yaml +++ b/test/e2e-v2/cases/dsl-debugging/mal/e2e.yaml @@ -50,7 +50,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/dsl-debugging/status >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin dsl-debug status >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/dsl-debugging/oal/dsl-debug-flow.sh b/test/e2e-v2/cases/dsl-debugging/oal/dsl-debug-flow.sh index 80027e186c..240b3c99f3 100755 --- a/test/e2e-v2/cases/dsl-debugging/oal/dsl-debug-flow.sh +++ b/test/e2e-v2/cases/dsl-debugging/oal/dsl-debug-flow.sh @@ -38,6 +38,11 @@ OAP_HOST="${OAP_HOST:-127.0.0.1}" OAP_REST_PORT="${OAP_REST_PORT:-17128}" OAP_BASE="http://${OAP_HOST}:${OAP_REST_PORT}" +# All admin-server REST calls go through swctl's `admin` command tree instead of +# raw curl. `--display json` keeps the body shape identical to the old curl +# output, so the downstream jq assertions are unchanged. +admin() { swctl --display json --admin-url="${OAP_BASE}" admin "$@"; } + SETTLE_SECONDS="${SETTLE_SECONDS:-300}" # OAL gate is per-metric. We target service_relation_server_cpm — a shipped @@ -51,20 +56,20 @@ CLIENT_ID="e2e-dsldbg-oal-1" log "waiting for OAP admin port" deadline=$(( $(date +%s) + 120 )) -until curl -fsS "${OAP_BASE}/dsl-debugging/status" >/dev/null 2>&1; do +until admin dsl-debug status >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP admin not ready after 120s"; fi sleep 2 done # --- Phase 0: status ------------------------------------------------------------------ -log "=== Phase 0: /dsl-debugging/status ===" -curl -fsS "${OAP_BASE}/dsl-debugging/status" | jq -e '.injectionEnabled == true' >/dev/null \ +log "=== Phase 0: dsl-debug status ===" +admin dsl-debug status | jq -e '.injectionEnabled == true' >/dev/null \ || fail "injectionEnabled is not true" # --- Phase 1: install session --------------------------------------------------------- log "=== Phase 1: install session on (${CATALOG}, ${NAME}, ${METRIC}) ===" -install_body="$(curl -fsS -XPOST \ - "${OAP_BASE}/dsl-debugging/session?catalog=${CATALOG}&name=${NAME}&ruleName=${METRIC}&clientId=${CLIENT_ID}")" +install_body="$(admin dsl-debug session start \ + --catalog "${CATALOG}" --name "${NAME}" --rule-name "${METRIC}" --client-id "${CLIENT_ID}")" log " install → ${install_body}" SESSION_ID="$(echo "${install_body}" | jq -r '.sessionId // empty')" [ -n "${SESSION_ID}" ] || fail "install did not return sessionId — body: ${install_body}" @@ -76,7 +81,7 @@ deadline=$(( $(date +%s) + SETTLE_SECONDS )) records_count=0 collect_body="" while (( $(date +%s) < deadline )); do - collect_body="$(curl -fsS "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}")" + collect_body="$(admin dsl-debug session get "${SESSION_ID}")" records_count="$(echo "${collect_body}" | jq '[.nodes[].records[]] | length')" if [ "${records_count}" -gt 0 ]; then # Wait for at least one execution record with both source + filter samples. @@ -197,7 +202,7 @@ log "✓ OAL shape valid (${records_count} records, ${total_samples} samples, so # --- Phase 5: stop session ------------------------------------------------------------ log "=== Phase 5: stop session ===" -curl -fsS -XPOST "${OAP_BASE}/dsl-debugging/session/${SESSION_ID}/stop" \ +admin dsl-debug session stop "${SESSION_ID}" \ | jq -e '.localStopped == true' >/dev/null \ || fail "localStopped != true" diff --git a/test/e2e-v2/cases/runtime-rule/cluster/cluster-flow.sh b/test/e2e-v2/cases/runtime-rule/cluster/cluster-flow.sh index bf2e7cf0e9..ac371559cb 100755 --- a/test/e2e-v2/cases/runtime-rule/cluster/cluster-flow.sh +++ b/test/e2e-v2/cases/runtime-rule/cluster/cluster-flow.sh @@ -49,9 +49,15 @@ CONVERGE_TIMEOUT_S="${CONVERGE_TIMEOUT_S:-90}" [ -f "${SEED_NEW}" ] || fail "seed-rule.yaml missing at ${SEED_NEW}" +# All runtime-rule REST calls go through swctl's `admin` command tree instead of +# raw curl. This flow drives two OAP nodes, so the admin host (`--admin-url`) is +# passed per call as the first argument. `--display json` keeps the body shape +# identical to the old curl output, so the jq assertions are unchanged. +admin() { local base="$1"; shift; swctl --display json --admin-url="${base}" admin "$@"; } + list_row() { local base="$1" - curl -fsS "${base}/runtime/rule/list" 2>/dev/null \ + admin "${base}" runtime-rule list 2>/dev/null \ | jq -c '.rules[] | select(.catalog == "'"${CATALOG}"'" and .name == "'"${NAME}"'") | select(.status != "n/a")' \ @@ -113,12 +119,9 @@ await_absent() { apply_on() { local base="$1" body="$2" extra="${3:-}" - local query="catalog=${CATALOG}&name=${NAME}" - if [ -n "${extra}" ]; then - query="${query}&${extra}" - fi - local resp; resp="$(curl -fsS -XPOST -H 'Content-Type: text/plain' \ - --data-binary "@${body}" "${base}/runtime/rule/addOrUpdate?${query}")" \ + local -a flags=(--catalog "${CATALOG}" --name "${NAME}" -f "${body}") + [[ "${extra}" == *allowStorageChange=true* ]] && flags+=(--allow-storage-change) + local resp; resp="$(admin "${base}" runtime-rule add "${flags[@]}")" \ || fail "addOrUpdate against ${base} failed" echo "${resp}" } @@ -126,13 +129,13 @@ apply_on() { # --- Wait for both OAPs to come up ------------------------------------------------- log "waiting for OAP-1 (${OAP1_BASE})" deadline=$(( $(date +%s) + 120 )) -until curl -fsS "${OAP1_BASE}/runtime/rule/list" >/dev/null 2>&1; do +until admin "${OAP1_BASE}" runtime-rule list >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP-1 not ready after 120s"; fi sleep 2 done log "waiting for OAP-2 (${OAP2_BASE})" deadline=$(( $(date +%s) + 120 )) -until curl -fsS "${OAP2_BASE}/runtime/rule/list" >/dev/null 2>&1; do +until admin "${OAP2_BASE}" runtime-rule list >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP-2 not ready after 120s"; fi sleep 2 done @@ -159,7 +162,7 @@ log "OAP-2 converged to ${hash_struct:0:8}…" # --- Phase 3: inactivate on OAP-1, observe INACTIVE on OAP-2 ----------------------- log "=== Phase 3: /inactivate on OAP-1 ===" -curl -fsS -XPOST "${OAP1_BASE}/runtime/rule/inactivate?catalog=${CATALOG}&name=${NAME}" >/dev/null \ +admin "${OAP1_BASE}" runtime-rule inactivate --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "inactivate against OAP-1 failed" await_status "${OAP1_BASE}" "INACTIVE" log "OAP-1 → INACTIVE" @@ -168,7 +171,7 @@ log "OAP-2 converged to INACTIVE" # --- Phase 4: delete on OAP-1, observe row gone on OAP-2 --------------------------- log "=== Phase 4: /delete on OAP-1 ===" -curl -fsS -XPOST "${OAP1_BASE}/runtime/rule/delete?catalog=${CATALOG}&name=${NAME}" >/dev/null \ +admin "${OAP1_BASE}" runtime-rule delete --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "delete against OAP-1 failed" await_absent "${OAP1_BASE}" log "OAP-1 → row gone" diff --git a/test/e2e-v2/cases/runtime-rule/cluster/e2e.yaml b/test/e2e-v2/cases/runtime-rule/cluster/e2e.yaml index 477a8a52b3..ff8d061cb5 100644 --- a/test/e2e-v2/cases/runtime-rule/cluster/e2e.yaml +++ b/test/e2e-v2/cases/runtime-rule/cluster/e2e.yaml @@ -42,7 +42,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/runtime/rule/list >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin runtime-rule list >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/runtime-rule/lal/e2e.yaml b/test/e2e-v2/cases/runtime-rule/lal/e2e.yaml index 3cc008a429..b02b7c2aa0 100644 --- a/test/e2e-v2/cases/runtime-rule/lal/e2e.yaml +++ b/test/e2e-v2/cases/runtime-rule/lal/e2e.yaml @@ -52,7 +52,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/runtime/rule/list >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin runtime-rule list >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/runtime-rule/lal/lal-flow.sh b/test/e2e-v2/cases/runtime-rule/lal/lal-flow.sh index 5c3a905869..aa57fd58bd 100755 --- a/test/e2e-v2/cases/runtime-rule/lal/lal-flow.sh +++ b/test/e2e-v2/cases/runtime-rule/lal/lal-flow.sh @@ -58,9 +58,14 @@ SETTLE_SECONDS="${SETTLE_SECONDS:-360}" [ -f "${SEED_V2}" ] || fail "seed v2 missing at ${SEED_V2}" [ -f "${SEED_MAL}" ] || fail "seed mal missing at ${SEED_MAL}" +# All runtime-rule REST calls go through swctl's `admin` command tree instead of +# raw curl. `--display json` keeps the response body shape identical to the old +# curl output, so the jq assertions below are unchanged. +admin() { swctl --display json --admin-url="${OAP_BASE}" admin "$@"; } + list_row() { local catalog="$1" name="$2" - curl -fsS "${OAP_BASE}/runtime/rule/list" 2>/dev/null \ + admin runtime-rule list 2>/dev/null \ | jq -c '.rules[] | select(.catalog == "'"${catalog}"'" and .name == "'"${name}"'") | select(.status != "n/a")' \ @@ -73,22 +78,20 @@ list_field() { apply_rule() { local catalog="$1" name="$2" body="$3" - curl -fsS -XPOST -H 'Content-Type: text/plain' \ - --data-binary "@${body}" \ - "${OAP_BASE}/runtime/rule/addOrUpdate?catalog=${catalog}&name=${name}" >/dev/null \ + admin runtime-rule add --catalog "${catalog}" --name "${name}" -f "${body}" >/dev/null \ || fail "addOrUpdate ${catalog}/${name} from ${body} failed" } # Retries 503 cluster_not_ready for up to 60s — the reconciler's peer-refresh # window briefly returns 503 right after a structural reshape (e.g. LAL -# delete that retires its dispatcher). Mirrors the MAL flow's pattern. -retry_post() { - local url="$1" +# delete that retires its dispatcher). Mirrors the MAL flow's pattern. Pass the +# runtime-rule subcommand and its flags. +retry_admin() { local deadline=$(( $(date +%s) + 60 )) local out while (( $(date +%s) < deadline )); do - out="$(curl -fsS -XPOST "${url}" 2>&1)" && return 0 - if [[ "${out}" == *503* ]]; then + out="$(admin "$@" 2>&1)" && return 0 + if echo "${out}" | grep -q "HTTP 503"; then sleep 2 continue fi @@ -101,13 +104,13 @@ retry_post() { inactivate_rule() { local catalog="$1" name="$2" - retry_post "${OAP_BASE}/runtime/rule/inactivate?catalog=${catalog}&name=${name}" >/dev/null \ + retry_admin runtime-rule inactivate --catalog "${catalog}" --name "${name}" >/dev/null \ || fail "inactivate ${catalog}/${name} failed" } delete_rule() { local catalog="$1" name="$2" - retry_post "${OAP_BASE}/runtime/rule/delete?catalog=${catalog}&name=${name}" >/dev/null \ + retry_admin runtime-rule delete --catalog "${catalog}" --name "${name}" >/dev/null \ || fail "delete ${catalog}/${name} failed" } @@ -143,7 +146,7 @@ await_metric_for_step() { log "waiting for OAP runtime-rule port" deadline=$(( $(date +%s) + 90 )) -until curl -fsS "${OAP_BASE}/runtime/rule/list" >/dev/null 2>&1; do +until admin runtime-rule list >/dev/null 2>&1; do if [ "$(date +%s)" -ge "${deadline}" ]; then fail "OAP not ready after 90s"; fi sleep 2 done diff --git a/test/e2e-v2/cases/runtime-rule/mal-storage/banyandb/e2e.yaml b/test/e2e-v2/cases/runtime-rule/mal-storage/banyandb/e2e.yaml index fb8033a540..9d09a5fc14 100644 --- a/test/e2e-v2/cases/runtime-rule/mal-storage/banyandb/e2e.yaml +++ b/test/e2e-v2/cases/runtime-rule/mal-storage/banyandb/e2e.yaml @@ -65,7 +65,7 @@ verify: # minimal so the harness reports the script's pass/fail directly. A trailing # /list smoke check confirms the receiver port is still serving after the # destructive phase (catches regressions where /delete crashes the handler). - - query: curl -fsS http://127.0.0.1:17128/runtime/rule/list >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin runtime-rule list >/dev/null && echo ok expected: expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/runtime-rule/mal-storage/elasticsearch/e2e.yaml b/test/e2e-v2/cases/runtime-rule/mal-storage/elasticsearch/e2e.yaml index 260773f1b2..d449ed83aa 100644 --- a/test/e2e-v2/cases/runtime-rule/mal-storage/elasticsearch/e2e.yaml +++ b/test/e2e-v2/cases/runtime-rule/mal-storage/elasticsearch/e2e.yaml @@ -56,7 +56,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/runtime/rule/list >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin runtime-rule list >/dev/null && echo ok expected: ../expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/runtime-rule/mal-storage/postgresql/e2e.yaml b/test/e2e-v2/cases/runtime-rule/mal-storage/postgresql/e2e.yaml index f15d0afd19..6562aae4ec 100644 --- a/test/e2e-v2/cases/runtime-rule/mal-storage/postgresql/e2e.yaml +++ b/test/e2e-v2/cases/runtime-rule/mal-storage/postgresql/e2e.yaml @@ -57,7 +57,7 @@ verify: count: 1 interval: 1s cases: - - query: curl -fsS http://127.0.0.1:17128/runtime/rule/list >/dev/null && echo ok + - query: swctl --display json --admin-url=http://127.0.0.1:17128 admin runtime-rule list >/dev/null && echo ok expected: ../expected/ok.txt cleanup: diff --git a/test/e2e-v2/cases/runtime-rule/mal-storage/runtime-rule-flow.sh b/test/e2e-v2/cases/runtime-rule/mal-storage/runtime-rule-flow.sh index 97831551aa..d9832ec10a 100755 --- a/test/e2e-v2/cases/runtime-rule/mal-storage/runtime-rule-flow.sh +++ b/test/e2e-v2/cases/runtime-rule/mal-storage/runtime-rule-flow.sh @@ -64,6 +64,14 @@ GQL_BASE="http://${OAP_HOST}:${OAP_GQL_PORT}" log() { echo "[runtime-rule-flow] $*" >&2; } fail() { echo "[runtime-rule-flow] FAIL: $*" >&2; exit 1; } +# Every runtime-rule REST call goes through swctl's `admin` command tree instead +# of raw curl. `--display json` keeps the response body byte-shape identical to +# the old curl output (the runtime-rule endpoints are passed through verbatim), +# so the jq assertions below are unchanged. On a non-2xx the CLI exits non-zero +# and renders the typed error envelope — `admin API <url>: HTTP <code> +# (<applyStatus>): <message>` — which the negative-path helpers grep for. +admin() { swctl --display json --admin-url="${REST_BASE}" admin "$@"; } + # Resolve the otlp-emitter container by name fragment so we don't need to know # the compose project name. Cached on first lookup. EMITTER_CONTAINER="" @@ -89,101 +97,103 @@ step_set() { log " step=${value}" } -# Retry a 2xx-or-fail curl for up to RETRY_BUDGET_S seconds. Exists because the -# cluster routing layer transiently returns 503 cluster_not_ready when its peer +# Retry a runtime-rule admin call for up to RETRY_BUDGET_S seconds. Exists because +# the cluster routing layer transiently returns 503 cluster_not_ready when its peer # refresh is in flight; happens reliably right after a STRUCTURAL apply (the # reconciler's cache may be paused). Operator retries after a few seconds work -# in practice, so the e2e applies the same pattern automatically. +# in practice, so the e2e applies the same pattern automatically. Pass the +# runtime-rule subcommand and its flags, e.g. +# retry_admin runtime-rule inactivate --catalog "${CATALOG}" --name "${NAME}" RETRY_BUDGET_S="${RETRY_BUDGET_S:-60}" -retry_curl_post() { - local url="$1" - local body_arg="${2:-}" # e.g. --data-binary @file ; empty for empty-body POST +retry_admin() { local deadline=$(( $(date +%s) + RETRY_BUDGET_S )) - local out + local out rc while (( $(date +%s) < deadline )); do - if [[ -n "${body_arg}" ]]; then - # shellcheck disable=SC2086 - out="$(curl -fsS -XPOST ${body_arg} -H "Content-Type: text/plain" "${url}" 2>&1)" && { - echo "${out}"; return 0; - } - else - out="$(curl -fsS -XPOST "${url}" 2>&1)" && { echo "${out}"; return 0; } - fi - if [[ "${out}" == *503* ]]; then - log " transient 503 on ${url} — retrying" + out="$(admin "$@" 2>&1)" && { echo "${out}"; return 0; } + rc=$? + if echo "${out}" | grep -q "HTTP 503"; then + log " transient 503 on 'admin $*' — retrying" sleep 2 continue fi echo "${out}" - return 1 + return "${rc}" done echo "${out}" return 1 } -# POST a rule file to /addOrUpdate. Echoes the JSON response. Asserts 200. +# Apply a rule file via addOrUpdate. Echoes the JSON response. Asserts 2xx. +# extra="allowStorageChange=true" maps to the --allow-storage-change flag. post_rule() { local file="$1" - local extra_qs="${2:-}" + local extra="${2:-}" local rule_name="${3:-${NAME}}" - local url="${REST_BASE}/runtime/rule/addOrUpdate?catalog=${CATALOG}&name=${rule_name}${extra_qs:+&${extra_qs}}" - log "POST ${url} (body=${file})" + local -a flags=(--catalog "${CATALOG}" --name "${rule_name}" -f "${file}") + [[ "${extra}" == *allowStorageChange=true* ]] && flags+=(--allow-storage-change) + log "runtime-rule add ${CATALOG}/${rule_name} (body=${file})" local resp - resp="$(curl -fsS -XPOST --data-binary "@${file}" -H "Content-Type: text/plain" "${url}")" \ + resp="$(admin runtime-rule add "${flags[@]}")" \ || fail "addOrUpdate of ${file} returned non-2xx" log " → ${resp}" echo "${resp}" } -# POST a rule that's expected to be REJECTED. Captures the HTTP status and the -# response body via curl's separate -w / -o, asserts the status matches, and -# echoes the body so callers can grep for a specific failure code/string. +# Apply a rule that's expected to be REJECTED. swctl exits non-zero on a non-2xx +# and renders the typed error envelope ("... HTTP <code> (<applyStatus>): <msg>") +# to stdout; assert the HTTP code is present and echo the message so callers can +# grep for a specific failure code / applyStatus / string. post_rule_expect_status() { local file="$1" local expected_status="$2" - local extra_qs="${3:-}" + local extra="${3:-}" local rule_name="${4:-${NAME}}" - local url="${REST_BASE}/runtime/rule/addOrUpdate?catalog=${CATALOG}&name=${rule_name}${extra_qs:+&${extra_qs}}" - log "POST ${url} (expect HTTP ${expected_status}, body=${file})" - local body_file http_status - body_file="$(mktemp)" - http_status="$(curl -sS -o "${body_file}" -w '%{http_code}' \ - -XPOST --data-binary "@${file}" -H "Content-Type: text/plain" "${url}")" - local body - body="$(cat "${body_file}")" - rm -f "${body_file}" - log " ← HTTP ${http_status} body=${body}" - [[ "${http_status}" == "${expected_status}" ]] \ - || fail "expected HTTP ${expected_status}, got ${http_status} (body: ${body})" - echo "${body}" + local -a flags=(--catalog "${CATALOG}" --name "${rule_name}" -f "${file}") + [[ "${extra}" == *allowStorageChange=true* ]] && flags+=(--allow-storage-change) + log "runtime-rule add ${CATALOG}/${rule_name} (expect HTTP ${expected_status}, body=${file})" + local out rc + out="$(admin runtime-rule add "${flags[@]}" 2>&1)" && rc=0 || rc=$? + log " ← rc=${rc} ${out}" + [[ "${rc}" -ne 0 ]] \ + || fail "expected rejection (HTTP ${expected_status}) but add succeeded: ${out}" + echo "${out}" | grep -q "HTTP ${expected_status}" \ + || fail "expected HTTP ${expected_status}, got: ${out}" + echo "${out}" } -# POST a non-/addOrUpdate endpoint that's expected to be REJECTED. Same -# semantics as post_rule_expect_status but takes an explicit URL. -post_url_expect_status() { - local url="$1" +# Delete a rule that's expected to be REJECTED (e.g. /delete on an ACTIVE row → +# 409 requires_inactivate_first). Same envelope-grep semantics as +# post_rule_expect_status. +delete_expect_status() { + local rule_name="$1" local expected_status="$2" - log "POST ${url} (expect HTTP ${expected_status})" - local body_file http_status - body_file="$(mktemp)" - http_status="$(curl -sS -o "${body_file}" -w '%{http_code}' -XPOST "${url}")" - local body - body="$(cat "${body_file}")" - rm -f "${body_file}" - log " ← HTTP ${http_status} body=${body}" - [[ "${http_status}" == "${expected_status}" ]] \ - || fail "expected HTTP ${expected_status}, got ${http_status} (body: ${body})" - echo "${body}" + log "runtime-rule delete ${CATALOG}/${rule_name} (expect HTTP ${expected_status})" + local out rc + out="$(admin runtime-rule delete --catalog "${CATALOG}" --name "${rule_name}" 2>&1)" && rc=0 || rc=$? + log " ← rc=${rc} ${out}" + [[ "${rc}" -ne 0 ]] \ + || fail "expected delete rejection (HTTP ${expected_status}) but it succeeded: ${out}" + echo "${out}" | grep -q "HTTP ${expected_status}" \ + || fail "expected HTTP ${expected_status}, got: ${out}" + echo "${out}" } -# Assert the JSON response carries the expected applyStatus. +# Assert the expected applyStatus. On the happy path the argument is the JSON +# ApplyResult and the status comes from .applyStatus. On a rejection the argument +# is swctl's error line, where the CLI's typed envelope renders the applyStatus in +# parentheses, e.g. "... HTTP 400 (layer_ordinal_out_of_range): <msg>". assert_apply_status() { local expected="$1" - local actual_json="$2" - local actual - actual="$(echo "${actual_json}" | jq -r '.applyStatus // empty')" - [[ "${actual}" == "${expected}" ]] \ - || fail "expected applyStatus=${expected}, got '${actual}' (full: ${actual_json})" + local actual="$2" + local parsed + parsed="$(echo "${actual}" | jq -r '.applyStatus // empty' 2>/dev/null || true)" + if [[ -n "${parsed}" ]]; then + [[ "${parsed}" == "${expected}" ]] \ + || fail "expected applyStatus=${expected}, got '${parsed}' (full: ${actual})" + return 0 + fi + echo "${actual}" | grep -q "(${expected})" \ + || fail "expected applyStatus=${expected}, not found in: ${actual}" } # GET /runtime/rule/list and ensure the row matches the expected status. Returns @@ -191,10 +201,10 @@ assert_apply_status() { list_row() { local expected_status="$1" local rule_name="${2:-${NAME}}" - log "GET /runtime/rule/list → looking for ${CATALOG}/${rule_name} status=${expected_status}" + log "runtime-rule list → looking for ${CATALOG}/${rule_name} status=${expected_status}" local lines - lines="$(curl -fsS "${REST_BASE}/runtime/rule/list")" \ - || fail "GET /runtime/rule/list failed" + lines="$(admin runtime-rule list)" \ + || fail "runtime-rule list failed" local match match="$(echo "${lines}" | jq -c ".rules[] | select(.catalog==\"${CATALOG}\" and .name==\"${rule_name}\")" 2>/dev/null || true)" [[ -n "${match}" ]] \ @@ -209,10 +219,10 @@ list_row() { # Assert that /list does NOT have a row for the given (catalog, name). list_no_row() { local rule_name="${1:-${NAME}}" - log "GET /runtime/rule/list → expect NO row for ${CATALOG}/${rule_name}" + log "runtime-rule list → expect NO row for ${CATALOG}/${rule_name}" local lines match - lines="$(curl -fsS "${REST_BASE}/runtime/rule/list")" \ - || fail "GET /runtime/rule/list failed" + lines="$(admin runtime-rule list)" \ + || fail "runtime-rule list failed" match="$(echo "${lines}" | jq -c ".rules[] | select(.catalog==\"${CATALOG}\" and .name==\"${rule_name}\")" 2>/dev/null || true)" if [[ -n "${match}" ]]; then local status @@ -404,8 +414,8 @@ assert_dump_contains() { shift local tar_file tar_file="$(mktemp)" - curl -fsS "${REST_BASE}/runtime/rule/dump" -o "${tar_file}" \ - || fail "GET /runtime/rule/dump failed (${label})" + admin runtime-rule dump -o "${tar_file}" >/dev/null \ + || fail "runtime-rule dump failed (${label})" local entries entries="$(tar -tzf "${tar_file}" 2>&1)" \ || { rm -f "${tar_file}"; fail "${label}: dump body is not a valid tar.gz: ${entries}"; } @@ -422,7 +432,7 @@ assert_dump_contains() { log "waiting for OAP runtime-rule port ${OAP_REST_PORT}" for _ in $(seq 1 60); do - curl -fsS "${REST_BASE}/runtime/rule/list" >/dev/null 2>&1 && break + admin runtime-rule list >/dev/null 2>&1 && break sleep 2 done @@ -495,7 +505,7 @@ assert_metric_step_advanced "e2e_rr_requests" "structural" "${struct_baseline}" log "=== Phase 5c: ILLEGAL /delete on ACTIVE row ===" struct_baseline="$(latest_bucket_id_for_step "e2e_rr_requests" "structural")" -post_url_expect_status "${REST_BASE}/runtime/rule/delete?catalog=${CATALOG}&name=${NAME}" "409" >/dev/null +delete_expect_status "${NAME}" "409" >/dev/null [[ "$(list_row ACTIVE | jq -r '.contentHash')" == "${hash_structural}" ]] \ || fail "5c: row state changed after /delete-on-ACTIVE rejection" assert_metric_step_advanced "e2e_rr_requests" "structural" "${struct_baseline}" 180 @@ -540,7 +550,9 @@ resp="$(post_rule_expect_status \ "${SEED_RULES_DIR}/illegal-layer-name-conflict.yaml" "400" "" "${SIBLING_NAME}")" assert_apply_status "layer_name_conflict" "${resp}" # Message must name the conflicting source so operators see what to align with. -echo "${resp}" | jq -e '.message | test("built-in")' >/dev/null \ +# resp is swctl's plain-text error envelope ("... (layer_name_conflict): <msg>"), +# not JSON, so grep the message directly rather than parsing it. +echo "${resp}" | grep -q "built-in" \ || fail "5g: response message did not label source as built-in: ${resp}" list_no_row "${SIBLING_NAME}" [[ "$(list_row ACTIVE | jq -r '.contentHash')" == "${hash_structural}" ]] \ @@ -576,13 +588,13 @@ oap_container="$(docker ps --filter "ancestor=skywalking/oap:latest" \ docker restart "${oap_container}" >/dev/null # Wait for the REST port to come back. Cap at 180s so a true hang surfaces. for i in $(seq 1 90); do - if curl -fsS "${REST_BASE}/runtime/rule/list" >/dev/null 2>&1; then + if admin runtime-rule list >/dev/null 2>&1; then log " OAP back up after ${i}*2s" break fi sleep 2 done -curl -fsS "${REST_BASE}/runtime/rule/list" >/dev/null \ +admin runtime-rule list >/dev/null \ || fail "5h: OAP did not come back online after restart" # Critical assertion: the runtime layer must still be visible AND retain its @@ -599,9 +611,9 @@ list_row "ACTIVE" "${SIBLING_NAME}" >/dev/null log " post-restart: layer + rule survived" # Now prove the layer is still removable through the dynamic channel. -retry_curl_post "${REST_BASE}/runtime/rule/inactivate?catalog=${CATALOG}&name=${SIBLING_NAME}" >/dev/null \ +retry_admin runtime-rule inactivate --catalog "${CATALOG}" --name "${SIBLING_NAME}" >/dev/null \ || fail "5h: sibling inactivate failed" -retry_curl_post "${REST_BASE}/runtime/rule/delete?catalog=${CATALOG}&name=${SIBLING_NAME}" >/dev/null \ +retry_admin runtime-rule delete --catalog "${CATALOG}" --name "${SIBLING_NAME}" >/dev/null \ || fail "5h: sibling delete failed" list_no_row "${SIBLING_NAME}" sleep 2 @@ -619,14 +631,12 @@ assert_metric_step_advanced "e2e_rr_requests" "structural" "${struct_baseline}" # POST a new shape under the same (catalog, name). log "=== Phase 6: SHAPE-BREAK ===" step_set "shape_break_old" -log " /inactivate to release the old shape" -inactivate_url="${REST_BASE}/runtime/rule/inactivate?catalog=${CATALOG}&name=${NAME}" -retry_curl_post "${inactivate_url}" >/dev/null \ +log " inactivate to release the old shape" +retry_admin runtime-rule inactivate --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "shape-break: inactivate failed" list_row "INACTIVE" >/dev/null -log " /delete to drop the old measure" -delete_url="${REST_BASE}/runtime/rule/delete?catalog=${CATALOG}&name=${NAME}" -retry_curl_post "${delete_url}" >/dev/null \ +log " delete to drop the old measure" +retry_admin runtime-rule delete --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "shape-break: delete failed" list_no_row @@ -647,7 +657,7 @@ await_metric_for_step "e2e_rr_requests" "shape_break_new" # window where the rule is still active aggregates a few `step=inactivate` # samples and the soft-pause assertion below fails for the wrong reason. log "=== Phase 7: INACTIVATE (soft-pause) ===" -retry_curl_post "${inactivate_url}" >/dev/null \ +retry_admin runtime-rule inactivate --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "phase-7: inactivate failed" list_row "INACTIVE" >/dev/null step_set "inactivate" @@ -673,10 +683,10 @@ await_metric_for_step "e2e_rr_requests" "activate" # Phase 9 — DELETE (destructive). log "=== Phase 9: DELETE ===" step_set "delete_attempt" -retry_curl_post "${inactivate_url}" >/dev/null \ +retry_admin runtime-rule inactivate --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "phase-9: inactivate-before-delete failed" list_row "INACTIVE" >/dev/null -retry_curl_post "${delete_url}" >/dev/null \ +retry_admin runtime-rule delete --catalog "${CATALOG}" --name "${NAME}" >/dev/null \ || fail "phase-9: delete failed" list_no_row log " ✓ row gone + backend probe agrees" diff --git a/test/e2e-v2/cases/storage/banyandb/e2e.yaml b/test/e2e-v2/cases/storage/banyandb/e2e.yaml index 9b33becacf..c59388a5d5 100644 --- a/test/e2e-v2/cases/storage/banyandb/e2e.yaml +++ b/test/e2e-v2/cases/storage/banyandb/e2e.yaml @@ -64,7 +64,7 @@ verify: - query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql tv2 ls --tags http.method=POST,http.status_code=201 expected: ../expected/empty-traces-v2-list.yml - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../expected/ttl-config-banyandb.yml cleanup: on: always diff --git a/test/e2e-v2/cases/storage/es/e2e.yaml b/test/e2e-v2/cases/storage/es/e2e.yaml index d8bb380b29..1e07021a0b 100644 --- a/test/e2e-v2/cases/storage/es/e2e.yaml +++ b/test/e2e-v2/cases/storage/es/e2e.yaml @@ -72,5 +72,5 @@ verify: ) expected: ../expected/trace-users-detail.yml - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../expected/ttl-config.yml \ No newline at end of file diff --git a/test/e2e-v2/cases/storage/es/es-sharding/e2e.yaml b/test/e2e-v2/cases/storage/es/es-sharding/e2e.yaml index f81c6ea828..cad4c17943 100644 --- a/test/e2e-v2/cases/storage/es/es-sharding/e2e.yaml +++ b/test/e2e-v2/cases/storage/es/es-sharding/e2e.yaml @@ -72,5 +72,5 @@ verify: ) expected: ../../expected/trace-users-detail.yml - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../../expected/ttl-config.yml \ No newline at end of file diff --git a/test/e2e-v2/cases/storage/expected/config-dump.yml b/test/e2e-v2/cases/storage/expected/config-dump.yml index 24cd41e5e5..a95bda9e18 100644 --- a/test/e2e-v2/cases/storage/expected/config-dump.yml +++ b/test/e2e-v2/cases/storage/expected/config-dump.yml @@ -13,216 +13,218 @@ # See the License for the specific language governing permissions and # limitations under the License. -admin-server.default.acceptQueueSize=0 -admin-server.default.contextPath=/ -admin-server.default.gRPCHost=0.0.0.0 -admin-server.default.gRPCMaxConcurrentCallsPerConnection=0 -admin-server.default.gRPCMaxMessageSize=52428800 -admin-server.default.gRPCPort=17129 -admin-server.default.gRPCSslCertChainPath= -admin-server.default.gRPCSslEnabled=false -admin-server.default.gRPCSslKeyPath= -admin-server.default.gRPCSslTrustedCAsPath= -admin-server.default.gRPCThreadPoolSize=0 -admin-server.default.host=0.0.0.0 -admin-server.default.httpMaxRequestHeaderSize=8192 -admin-server.default.idleTimeOut=30000 -admin-server.default.internalCommunicationTimeout=5000 -admin-server.default.port=17128 -admin-server.provider=default -agent-analyzer.default.forceSampleErrorSegment=true -agent-analyzer.default.meterAnalyzerActiveFiles=datasource,threadpool,satellite,go-runtime,python-runtime,continuous-profiling,java-agent,go-agent,ruby-runtime -agent-analyzer.default.noUpstreamRealAddressAgents=6000,9000 -agent-analyzer.default.segmentStatusAnalysisStrategy=FROM_SPAN_STATUS -agent-analyzer.default.slowCacheReadThreshold=default:20,redis:10 -agent-analyzer.default.slowCacheWriteThreshold=default:20,redis:10 -agent-analyzer.default.slowDBAccessThreshold=default:200,mongodb:100 -agent-analyzer.default.traceSamplingPolicySettingsFile=trace-sampling-policy-settings.yml -agent-analyzer.provider=default -ai-pipeline.default.baselineServerAddr= -ai-pipeline.default.baselineServerPort=18080 -ai-pipeline.default.uriRecognitionServerAddr= -ai-pipeline.default.uriRecognitionServerPort=17128 -ai-pipeline.provider=default -alarm.provider=default -aws-firehose.default.acceptQueueSize=0 -aws-firehose.default.contextPath=/ -aws-firehose.default.enableTLS=false -aws-firehose.default.firehoseAccessKey=****** -aws-firehose.default.host=0.0.0.0 -aws-firehose.default.idleTimeOut=30000 -aws-firehose.default.maxRequestHeaderSize=8192 -aws-firehose.default.port=12801 -aws-firehose.default.tlsCertChainPath= -aws-firehose.default.tlsKeyPath= -aws-firehose.provider=default -cluster.provider=standalone -configuration-discovery.default.disableMessageDigest=false -configuration-discovery.provider=default -configuration.provider=none -core.default.activeExtraModelColumns=false -core.default.autocompleteTagKeysQueryMaxSize=100 -core.default.autocompleteTagValuesQueryMaxSize=100 -core.default.dataKeeperExecutePeriod=5 -core.default.downsampling=[Hour, Day] -core.default.enableDataKeeperExecutor=true -core.default.enableEndpointNameGroupingByOpenapi=true -core.default.enableHierarchy=true -core.default.endpointNameMaxLength=150 -core.default.gRPCHost=0.0.0.0 -core.default.gRPCPort=11800 -core.default.gRPCSslCertChainPath= -core.default.gRPCSslEnabled=false -core.default.gRPCSslKeyPath= -core.default.gRPCSslTrustedCAPath= -core.default.gRPCThreadPoolSize=-1 -core.default.httpMaxRequestHeaderSize=8192 -core.default.instanceNameMaxLength=70 -core.default.l1FlushPeriod=500 -core.default.maxConcurrentCallsPerConnection=0 -core.default.maxDirectMemoryUsage=-1 -core.default.maxHeapMemoryUsagePercent=96 -core.default.maxHttpUrisNumberPerService=3000 -core.default.maxMessageSize=52428800 -core.default.metricsDataTTL=7 -core.default.persistentPeriod=25 -core.default.prepareThreads=2 -core.default.recordDataTTL=3 -core.default.restAcceptQueueSize=0 -core.default.restContextPath=/ -core.default.restHost=0.0.0.0 -core.default.restIdleTimeOut=30000 -core.default.restPort=12800 -core.default.role=Mixed -core.default.searchableAlarmTags=level -core.default.searchableLogsTags=level,http.status_code,ai_route_type -core.default.searchableTracesTags=http.method,http.status_code,rpc.status_code,db.type,db.instance,mq.queue,mq.topic,mq.broker -core.default.serviceCacheRefreshInterval=10 -core.default.serviceNameMaxLength=70 -core.default.storageSessionTimeout=70000 -core.default.syncPeriodHttpUriRecognitionPattern=10 -core.default.topNReportPeriod=10 -core.default.trainingPeriodHttpUriRecognitionPattern=60 -core.provider=default -dsl-debugging.default.injectionEnabled=true -dsl-debugging.provider=default -envoy-metric.default.acceptMetricsService=true -envoy-metric.default.alsHTTPAnalysis= -envoy-metric.default.alsTCPAnalysis= -envoy-metric.default.enabledEnvoyMetricsRules=envoy,envoy-svc-relation -envoy-metric.default.gRPCHost=0.0.0.0 -envoy-metric.default.gRPCPort=0 -envoy-metric.default.gRPCSslCertChainPath= -envoy-metric.default.gRPCSslEnabled=false -envoy-metric.default.gRPCSslKeyPath= -envoy-metric.default.gRPCSslTrustedCAsPath= -envoy-metric.default.gRPCThreadPoolSize=0 -envoy-metric.default.istioServiceEntryIgnoredNamespaces= -envoy-metric.default.istioServiceNameRule=${serviceEntry.metadata.name}.${serviceEntry.metadata.namespace} -envoy-metric.default.k8sServiceNameRule=${pod.metadata.labels.(service.istio.io/canonical-name)}.${pod.metadata.namespace} -envoy-metric.default.maxConcurrentCallsPerConnection=0 -envoy-metric.default.maxMessageSize=0 -envoy-metric.provider=default -event-analyzer.provider=default -gen-ai-analyzer.provider=default -health-checker.default.checkIntervalSeconds=30 -health-checker.provider=default -inspect.provider=default -log-analyzer.default.lalFiles=envoy-als,mesh-dp,mysql-slowsql,pgsql-slowsql,redis-slowsql,k8s-service,nginx,envoy-ai-gateway,miniprogram,default -log-analyzer.default.malFiles=nginx,miniprogram-wechat,miniprogram-alipay -log-analyzer.provider=default -logql.default.restAcceptQueueSize=0 -logql.default.restContextPath=/ -logql.default.restHost=0.0.0.0 -logql.default.restIdleTimeOut=30000 -logql.default.restPort=3100 -logql.provider=default -promql.default.buildInfoBranch= -promql.default.buildInfoBuildDate= -promql.default.buildInfoBuildUser=****** -promql.default.buildInfoGoVersion= -promql.default.buildInfoRevision= -promql.default.buildInfoVersion=2.45.0 -promql.default.restAcceptQueueSize=0 -promql.default.restContextPath=/ -promql.default.restHost=0.0.0.0 -promql.default.restIdleTimeOut=30000 -promql.default.restPort=9090 -promql.provider=default -query.graphql.enableLogTestTool=false -query.graphql.enableOnDemandPodLog=false -query.graphql.maxQueryComplexity=3000 -query.provider=graphql -receiver-async-profiler.default.jfrMaxSize=31457280 -receiver-async-profiler.default.memoryParserEnabled=true -receiver-async-profiler.provider=default -receiver-browser.default.sampleRate=10000 -receiver-browser.provider=default -receiver-clr.provider=default -receiver-ebpf.default.continuousPolicyCacheTimeout=60 -receiver-ebpf.default.gRPCHost=0.0.0.0 -receiver-ebpf.default.gRPCPort=0 -receiver-ebpf.default.gRPCSslCertChainPath= -receiver-ebpf.default.gRPCSslEnabled=false -receiver-ebpf.default.gRPCSslKeyPath= -receiver-ebpf.default.gRPCSslTrustedCAsPath= -receiver-ebpf.default.gRPCThreadPoolSize=0 -receiver-ebpf.default.maxConcurrentCallsPerConnection=0 -receiver-ebpf.default.maxMessageSize=0 -receiver-ebpf.provider=default -receiver-event.provider=default -receiver-jvm.provider=default -receiver-log.provider=default -receiver-meter.provider=default -receiver-otel.default.enabledHandlers=otlp-traces,otlp-metrics,otlp-logs -receiver-otel.default.enabledOtelMetricsRules=apisix,nginx/*,k8s/*,istio-controlplane,vm,mysql/*,postgresql/*,oap,aws-eks/*,windows,aws-s3/*,aws-dynamodb/*,aws-gateway/*,redis/*,elasticsearch/*,rabbitmq/*,mongodb/*,kafka/*,pulsar/*,bookkeeper/*,rocketmq/*,clickhouse/*,activemq/*,kong/*,flink/*,banyandb/*,envoy-ai-gateway/*,ios/*,miniprogram/* -receiver-otel.provider=default -receiver-pprof.default.memoryParserEnabled=true -receiver-pprof.default.pprofMaxSize=31457280 -receiver-pprof.provider=default -receiver-profile.provider=default -receiver-register.provider=default -receiver-runtime-rule.default.refreshRulesPeriod=30 -receiver-runtime-rule.default.selfHealThresholdSeconds=60 -receiver-runtime-rule.provider=default -receiver-sharing-server.default.authentication=****** -receiver-sharing-server.default.gRPCHost=0.0.0.0 -receiver-sharing-server.default.gRPCPort=0 -receiver-sharing-server.default.gRPCSslCertChainPath= -receiver-sharing-server.default.gRPCSslEnabled=false -receiver-sharing-server.default.gRPCSslKeyPath= -receiver-sharing-server.default.gRPCSslTrustedCAsPath= -receiver-sharing-server.default.gRPCThreadPoolSize=0 -receiver-sharing-server.default.httpMaxRequestHeaderSize=8192 -receiver-sharing-server.default.maxConcurrentCallsPerConnection=0 -receiver-sharing-server.default.maxMessageSize=52428800 -receiver-sharing-server.default.restAcceptQueueSize=0 -receiver-sharing-server.default.restContextPath=/ -receiver-sharing-server.default.restHost=0.0.0.0 -receiver-sharing-server.default.restIdleTimeOut=30000 -receiver-sharing-server.default.restPort=0 -receiver-sharing-server.provider=default -receiver-telegraf.default.activeFiles=vm -receiver-telegraf.provider=default -receiver-trace.provider=default -service-mesh.provider=default -status.default.keywords4MaskingSecretsOfConfig=user,password,trustStorePass,keyStorePass,token,accessKey,secretKey,authentication -status.provider=default -storage.mysql.asyncBatchPersistentPoolSize=4 -storage.mysql.maxSizeOfBatchSql=2000 -storage.mysql.metadataQueryMaxSize=5000 -storage.mysql.properties.dataSource.cachePrepStmts=true -storage.mysql.properties.dataSource.password=****** -storage.mysql.properties.dataSource.prepStmtCacheSize=250 -storage.mysql.properties.dataSource.prepStmtCacheSqlLimit=2048 -storage.mysql.properties.dataSource.useServerPrepStmts=true -storage.mysql.properties.dataSource.user=****** -storage.mysql.properties.jdbcUrl=jdbc:mysql://mysql:3306/swtest?allowMultiQueries=true -storage.provider=mysql -telemetry.prometheus.host=0.0.0.0 -telemetry.prometheus.port=1234 -telemetry.prometheus.sslCertChainPath= -telemetry.prometheus.sslEnabled=false -telemetry.prometheus.sslKeyPath= -telemetry.provider=prometheus -ui-management.provider=default +{ + "admin-server.default.acceptQueueSize": "0", + "admin-server.default.contextPath": "/", + "admin-server.default.gRPCHost": "0.0.0.0", + "admin-server.default.gRPCMaxConcurrentCallsPerConnection": "0", + "admin-server.default.gRPCMaxMessageSize": "52428800", + "admin-server.default.gRPCPort": "17129", + "admin-server.default.gRPCSslCertChainPath": "", + "admin-server.default.gRPCSslEnabled": "false", + "admin-server.default.gRPCSslKeyPath": "", + "admin-server.default.gRPCSslTrustedCAsPath": "", + "admin-server.default.gRPCThreadPoolSize": "0", + "admin-server.default.host": "0.0.0.0", + "admin-server.default.httpMaxRequestHeaderSize": "8192", + "admin-server.default.idleTimeOut": "30000", + "admin-server.default.internalCommunicationTimeout": "5000", + "admin-server.default.port": "17128", + "admin-server.provider": "default", + "agent-analyzer.default.forceSampleErrorSegment": "true", + "agent-analyzer.default.meterAnalyzerActiveFiles": "datasource,threadpool,satellite,go-runtime,python-runtime,continuous-profiling,java-agent,go-agent,ruby-runtime", + "agent-analyzer.default.noUpstreamRealAddressAgents": "6000,9000", + "agent-analyzer.default.segmentStatusAnalysisStrategy": "FROM_SPAN_STATUS", + "agent-analyzer.default.slowCacheReadThreshold": "default:20,redis:10", + "agent-analyzer.default.slowCacheWriteThreshold": "default:20,redis:10", + "agent-analyzer.default.slowDBAccessThreshold": "default:200,mongodb:100", + "agent-analyzer.default.traceSamplingPolicySettingsFile": "trace-sampling-policy-settings.yml", + "agent-analyzer.provider": "default", + "ai-pipeline.default.baselineServerAddr": "", + "ai-pipeline.default.baselineServerPort": "18080", + "ai-pipeline.default.uriRecognitionServerAddr": "", + "ai-pipeline.default.uriRecognitionServerPort": "17128", + "ai-pipeline.provider": "default", + "alarm.provider": "default", + "aws-firehose.default.acceptQueueSize": "0", + "aws-firehose.default.contextPath": "/", + "aws-firehose.default.enableTLS": "false", + "aws-firehose.default.firehoseAccessKey": "******", + "aws-firehose.default.host": "0.0.0.0", + "aws-firehose.default.idleTimeOut": "30000", + "aws-firehose.default.maxRequestHeaderSize": "8192", + "aws-firehose.default.port": "12801", + "aws-firehose.default.tlsCertChainPath": "", + "aws-firehose.default.tlsKeyPath": "", + "aws-firehose.provider": "default", + "cluster.provider": "standalone", + "configuration-discovery.default.disableMessageDigest": "false", + "configuration-discovery.provider": "default", + "configuration.provider": "none", + "core.default.activeExtraModelColumns": "false", + "core.default.autocompleteTagKeysQueryMaxSize": "100", + "core.default.autocompleteTagValuesQueryMaxSize": "100", + "core.default.dataKeeperExecutePeriod": "5", + "core.default.downsampling": "[Hour, Day]", + "core.default.enableDataKeeperExecutor": "true", + "core.default.enableEndpointNameGroupingByOpenapi": "true", + "core.default.enableHierarchy": "true", + "core.default.endpointNameMaxLength": "150", + "core.default.gRPCHost": "0.0.0.0", + "core.default.gRPCPort": "11800", + "core.default.gRPCSslCertChainPath": "", + "core.default.gRPCSslEnabled": "false", + "core.default.gRPCSslKeyPath": "", + "core.default.gRPCSslTrustedCAPath": "", + "core.default.gRPCThreadPoolSize": "-1", + "core.default.httpMaxRequestHeaderSize": "8192", + "core.default.instanceNameMaxLength": "70", + "core.default.l1FlushPeriod": "500", + "core.default.maxConcurrentCallsPerConnection": "0", + "core.default.maxDirectMemoryUsage": "-1", + "core.default.maxHeapMemoryUsagePercent": "96", + "core.default.maxHttpUrisNumberPerService": "3000", + "core.default.maxMessageSize": "52428800", + "core.default.metricsDataTTL": "7", + "core.default.persistentPeriod": "25", + "core.default.prepareThreads": "2", + "core.default.recordDataTTL": "3", + "core.default.restAcceptQueueSize": "0", + "core.default.restContextPath": "/", + "core.default.restHost": "0.0.0.0", + "core.default.restIdleTimeOut": "30000", + "core.default.restPort": "12800", + "core.default.role": "Mixed", + "core.default.searchableAlarmTags": "level", + "core.default.searchableLogsTags": "level,http.status_code,ai_route_type", + "core.default.searchableTracesTags": "http.method,http.status_code,rpc.status_code,db.type,db.instance,mq.queue,mq.topic,mq.broker", + "core.default.serviceCacheRefreshInterval": "10", + "core.default.serviceNameMaxLength": "70", + "core.default.storageSessionTimeout": "70000", + "core.default.syncPeriodHttpUriRecognitionPattern": "10", + "core.default.topNReportPeriod": "10", + "core.default.trainingPeriodHttpUriRecognitionPattern": "60", + "core.provider": "default", + "dsl-debugging.default.injectionEnabled": "true", + "dsl-debugging.provider": "default", + "envoy-metric.default.acceptMetricsService": "true", + "envoy-metric.default.alsHTTPAnalysis": "", + "envoy-metric.default.alsTCPAnalysis": "", + "envoy-metric.default.enabledEnvoyMetricsRules": "envoy,envoy-svc-relation", + "envoy-metric.default.gRPCHost": "0.0.0.0", + "envoy-metric.default.gRPCPort": "0", + "envoy-metric.default.gRPCSslCertChainPath": "", + "envoy-metric.default.gRPCSslEnabled": "false", + "envoy-metric.default.gRPCSslKeyPath": "", + "envoy-metric.default.gRPCSslTrustedCAsPath": "", + "envoy-metric.default.gRPCThreadPoolSize": "0", + "envoy-metric.default.istioServiceEntryIgnoredNamespaces": "", + "envoy-metric.default.istioServiceNameRule": "${serviceEntry.metadata.name}.${serviceEntry.metadata.namespace}", + "envoy-metric.default.k8sServiceNameRule": "${pod.metadata.labels.(service.istio.io/canonical-name)}.${pod.metadata.namespace}", + "envoy-metric.default.maxConcurrentCallsPerConnection": "0", + "envoy-metric.default.maxMessageSize": "0", + "envoy-metric.provider": "default", + "event-analyzer.provider": "default", + "gen-ai-analyzer.provider": "default", + "health-checker.default.checkIntervalSeconds": "30", + "health-checker.provider": "default", + "inspect.provider": "default", + "log-analyzer.default.lalFiles": "envoy-als,mesh-dp,mysql-slowsql,pgsql-slowsql,redis-slowsql,k8s-service,nginx,envoy-ai-gateway,miniprogram,default", + "log-analyzer.default.malFiles": "nginx,miniprogram-wechat,miniprogram-alipay", + "log-analyzer.provider": "default", + "logql.default.restAcceptQueueSize": "0", + "logql.default.restContextPath": "/", + "logql.default.restHost": "0.0.0.0", + "logql.default.restIdleTimeOut": "30000", + "logql.default.restPort": "3100", + "logql.provider": "default", + "promql.default.buildInfoBranch": "", + "promql.default.buildInfoBuildDate": "", + "promql.default.buildInfoBuildUser": "******", + "promql.default.buildInfoGoVersion": "", + "promql.default.buildInfoRevision": "", + "promql.default.buildInfoVersion": "2.45.0", + "promql.default.restAcceptQueueSize": "0", + "promql.default.restContextPath": "/", + "promql.default.restHost": "0.0.0.0", + "promql.default.restIdleTimeOut": "30000", + "promql.default.restPort": "9090", + "promql.provider": "default", + "query.graphql.enableLogTestTool": "false", + "query.graphql.enableOnDemandPodLog": "false", + "query.graphql.maxQueryComplexity": "3000", + "query.provider": "graphql", + "receiver-async-profiler.default.jfrMaxSize": "31457280", + "receiver-async-profiler.default.memoryParserEnabled": "true", + "receiver-async-profiler.provider": "default", + "receiver-browser.default.sampleRate": "10000", + "receiver-browser.provider": "default", + "receiver-clr.provider": "default", + "receiver-ebpf.default.continuousPolicyCacheTimeout": "60", + "receiver-ebpf.default.gRPCHost": "0.0.0.0", + "receiver-ebpf.default.gRPCPort": "0", + "receiver-ebpf.default.gRPCSslCertChainPath": "", + "receiver-ebpf.default.gRPCSslEnabled": "false", + "receiver-ebpf.default.gRPCSslKeyPath": "", + "receiver-ebpf.default.gRPCSslTrustedCAsPath": "", + "receiver-ebpf.default.gRPCThreadPoolSize": "0", + "receiver-ebpf.default.maxConcurrentCallsPerConnection": "0", + "receiver-ebpf.default.maxMessageSize": "0", + "receiver-ebpf.provider": "default", + "receiver-event.provider": "default", + "receiver-jvm.provider": "default", + "receiver-log.provider": "default", + "receiver-meter.provider": "default", + "receiver-otel.default.enabledHandlers": "otlp-traces,otlp-metrics,otlp-logs", + "receiver-otel.default.enabledOtelMetricsRules": "apisix,nginx/*,k8s/*,istio-controlplane,vm,mysql/*,postgresql/*,oap,aws-eks/*,windows,aws-s3/*,aws-dynamodb/*,aws-gateway/*,redis/*,elasticsearch/*,rabbitmq/*,mongodb/*,kafka/*,pulsar/*,bookkeeper/*,rocketmq/*,clickhouse/*,activemq/*,kong/*,flink/*,banyandb/*,envoy-ai-gateway/*,ios/*,miniprogram/*", + "receiver-otel.provider": "default", + "receiver-pprof.default.memoryParserEnabled": "true", + "receiver-pprof.default.pprofMaxSize": "31457280", + "receiver-pprof.provider": "default", + "receiver-profile.provider": "default", + "receiver-register.provider": "default", + "receiver-runtime-rule.default.refreshRulesPeriod": "30", + "receiver-runtime-rule.default.selfHealThresholdSeconds": "60", + "receiver-runtime-rule.provider": "default", + "receiver-sharing-server.default.authentication": "******", + "receiver-sharing-server.default.gRPCHost": "0.0.0.0", + "receiver-sharing-server.default.gRPCPort": "0", + "receiver-sharing-server.default.gRPCSslCertChainPath": "", + "receiver-sharing-server.default.gRPCSslEnabled": "false", + "receiver-sharing-server.default.gRPCSslKeyPath": "", + "receiver-sharing-server.default.gRPCSslTrustedCAsPath": "", + "receiver-sharing-server.default.gRPCThreadPoolSize": "0", + "receiver-sharing-server.default.httpMaxRequestHeaderSize": "8192", + "receiver-sharing-server.default.maxConcurrentCallsPerConnection": "0", + "receiver-sharing-server.default.maxMessageSize": "52428800", + "receiver-sharing-server.default.restAcceptQueueSize": "0", + "receiver-sharing-server.default.restContextPath": "/", + "receiver-sharing-server.default.restHost": "0.0.0.0", + "receiver-sharing-server.default.restIdleTimeOut": "30000", + "receiver-sharing-server.default.restPort": "0", + "receiver-sharing-server.provider": "default", + "receiver-telegraf.default.activeFiles": "vm", + "receiver-telegraf.provider": "default", + "receiver-trace.provider": "default", + "service-mesh.provider": "default", + "status.default.keywords4MaskingSecretsOfConfig": "user,password,trustStorePass,keyStorePass,token,accessKey,secretKey,authentication", + "status.provider": "default", + "storage.mysql.asyncBatchPersistentPoolSize": "4", + "storage.mysql.maxSizeOfBatchSql": "2000", + "storage.mysql.metadataQueryMaxSize": "5000", + "storage.mysql.properties.dataSource.cachePrepStmts": "true", + "storage.mysql.properties.dataSource.password": "******", + "storage.mysql.properties.dataSource.prepStmtCacheSize": "250", + "storage.mysql.properties.dataSource.prepStmtCacheSqlLimit": "2048", + "storage.mysql.properties.dataSource.useServerPrepStmts": "true", + "storage.mysql.properties.dataSource.user": "******", + "storage.mysql.properties.jdbcUrl": "jdbc:mysql://mysql:3306/swtest?allowMultiQueries=true", + "storage.provider": "mysql", + "telemetry.prometheus.host": "0.0.0.0", + "telemetry.prometheus.port": "1234", + "telemetry.prometheus.sslCertChainPath": "", + "telemetry.prometheus.sslEnabled": "false", + "telemetry.prometheus.sslKeyPath": "", + "telemetry.provider": "prometheus", + "ui-management.provider": "default" +} diff --git a/test/e2e-v2/cases/storage/mysql/e2e.yaml b/test/e2e-v2/cases/storage/mysql/e2e.yaml index c6f14c5162..423c3c6024 100644 --- a/test/e2e-v2/cases/storage/mysql/e2e.yaml +++ b/test/e2e-v2/cases/storage/mysql/e2e.yaml @@ -69,8 +69,7 @@ verify: | yq e '.traces[0].traceids[0]' - \ ) expected: ../expected/trace-users-detail.yml - - query: curl -X GET http://${oap_host}:${oap_17128}/debugging/config/dump + - query: swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config dump expected: ../expected/config-dump.yml - - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + - query: swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../expected/ttl-config.yml \ No newline at end of file diff --git a/test/e2e-v2/cases/storage/opensearch/e2e.yaml b/test/e2e-v2/cases/storage/opensearch/e2e.yaml index d8bb380b29..1e07021a0b 100644 --- a/test/e2e-v2/cases/storage/opensearch/e2e.yaml +++ b/test/e2e-v2/cases/storage/opensearch/e2e.yaml @@ -72,5 +72,5 @@ verify: ) expected: ../expected/trace-users-detail.yml - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../expected/ttl-config.yml \ No newline at end of file diff --git a/test/e2e-v2/cases/storage/postgres/e2e.yaml b/test/e2e-v2/cases/storage/postgres/e2e.yaml index 5fe085caf1..bbc791aeb1 100644 --- a/test/e2e-v2/cases/storage/postgres/e2e.yaml +++ b/test/e2e-v2/cases/storage/postgres/e2e.yaml @@ -70,5 +70,5 @@ verify: ) expected: ../expected/trace-users-detail.yml - query: | - curl -X GET http://${oap_host}:${oap_17128}/status/config/ttl -H "Accept: application/json" + swctl --display json --admin-url=http://${oap_host}:${oap_17128} admin config ttl expected: ../expected/ttl-config.yml \ No newline at end of file diff --git a/test/e2e-v2/cases/storage/storage-cases.yaml b/test/e2e-v2/cases/storage/storage-cases.yaml index 36203e5677..90f851644b 100644 --- a/test/e2e-v2/cases/storage/storage-cases.yaml +++ b/test/e2e-v2/cases/storage/storage-cases.yaml @@ -167,58 +167,61 @@ cases: # PostgreSQL). # ===================================================================== - # /inspect/metrics — catalog endpoint, asserts service_cpm shows the + # inspect metrics — catalog endpoint, asserts service_cpm shows the # expected type / scope / catalog / supported downsamplings. - - query: curl -s "http://${oap_host}:${oap_17128}/inspect/metrics?regex=service_cpm" | yq -P + - query: swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect metrics --regex service_cpm expected: expected/inspect-metrics.yml - # /inspect/entities — Service scope, step=DAY (single bucket = today UTC). + # inspect entities — Service scope, step=DAY (single bucket = today UTC). # Avoids the framework's MAX_TIME_RANGE=500 cap and works on any host shell # (BSD date on macOS, GNU date in CI; both accept `date -u +%Y-%m-%d`). - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_cpm&start=${DAY}&end=${DAY}&step=DAY" | yq -P + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_cpm --start "${DAY}" --end "${DAY}" --step DAY expected: expected/inspect-entities-service-cpm.yml # ServiceInstance scope — instance-id decode + parent-service layer lookup. - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_instance_cpm&start=${DAY}&end=${DAY}&step=DAY" | yq -P + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_instance_cpm --start "${DAY}" --end "${DAY}" --step DAY expected: expected/inspect-entities-service-instance-cpm.yml # Endpoint scope — endpoint-id decode. - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=endpoint_cpm&start=${DAY}&end=${DAY}&step=DAY" | yq -P + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric endpoint_cpm --start "${DAY}" --end "${DAY}" --step DAY expected: expected/inspect-entities-endpoint-cpm.yml # ServiceRelation scope — analysisRelationId decode + paired source/destination # shape + MqeEntity dest* fields. - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_relation_client_cpm&start=${DAY}&end=${DAY}&step=DAY" | yq -P + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_relation_client_cpm --start "${DAY}" --end "${DAY}" --step DAY expected: expected/inspect-entities-service-relation-cpm.yml # LABELED_VALUE metric — Service-scope entity decode is identical but the # storage value column shape differs (BanyanDB DataTable, ES JSON, JDBC # VARCHAR). - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_percentile&start=${DAY}&end=${DAY}&step=DAY" | yq -P + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_percentile --start "${DAY}" --end "${DAY}" --step DAY expected: expected/inspect-entities-service-percentile.yml # step=MINUTE over a 20-minute window. Exercises minute-precision range. # python3 keeps the date math portable across BSD / GNU date. - query: | - START=$(python3 -c "import datetime as d; print((d.datetime.utcnow() - d.timedelta(minutes=20)).strftime('%Y-%m-%d %H%M'))" | sed 's/ /%20/') - END=$(python3 -c "import datetime as d; print(d.datetime.utcnow().strftime('%Y-%m-%d %H%M'))" | sed 's/ /%20/') - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_cpm&start=${START}&end=${END}&step=MINUTE" | yq -P + START=$(python3 -c "import datetime as d; print((d.datetime.utcnow() - d.timedelta(minutes=20)).strftime('%Y-%m-%d %H%M'))") + END=$(python3 -c "import datetime as d; print(d.datetime.utcnow().strftime('%Y-%m-%d %H%M'))") + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_cpm --start "${START}" --end "${END}" --step MINUTE expected: expected/inspect-entities-service-cpm-minute.yml # step=HOUR over a full-day window (00 → 23 UTC). Avoids the flakiness of # a sliding 2-hour window — postgres / slow downsamplers may not have # populated the *current* HOUR bucket yet, but at least one earlier HOUR # bucket in today is guaranteed once the MINUTE case has data. - query: | - START=$(date -u +"%Y-%m-%d")%2000 - END=$(date -u +"%Y-%m-%d")%2023 - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=service_cpm&start=${START}&end=${END}&step=HOUR" | yq -P + DAY=$(date -u +"%Y-%m-%d") + swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric service_cpm --start "${DAY} 00" --end "${DAY} 23" --step HOUR expected: expected/inspect-entities-service-cpm-hour.yml - # Negative — unknown metric returns a 400-shaped error JSON. + # Negative — unknown metric: swctl exits non-zero and renders the typed error + # envelope ("... HTTP 400: unknown metric: <name>"). Reconstruct the original + # {error: ...} shape from the message so the expected file is unchanged. - query: | DAY=$(date -u +"%Y-%m-%d") - curl -s "http://${oap_host}:${oap_17128}/inspect/entities?metric=nonexistent_metric_xyz&start=${DAY}&end=${DAY}&step=DAY" | yq -P + out=$(swctl --display yaml --admin-url=http://${oap_host}:${oap_17128} admin inspect entities --metric nonexistent_metric_xyz --start "${DAY}" --end "${DAY}" --step DAY 2>&1 || true) + msg=$(echo "$out" | grep -o 'unknown metric: [a-z_0-9]*' | head -1) + yq -n ".error = \"${msg}\"" expected: expected/inspect-entities-unknown-metric.yml diff --git a/test/e2e-v2/cases/ui-management/ui-management-cases.yaml b/test/e2e-v2/cases/ui-management/ui-management-cases.yaml index 82aca58ce0..bc572ce17f 100644 --- a/test/e2e-v2/cases/ui-management/ui-management-cases.yaml +++ b/test/e2e-v2/cases/ui-management/ui-management-cases.yaml @@ -13,61 +13,60 @@ # See the License for the specific language governing permissions and # limitations under the License. -# REST surface for dashboard templates, mounted on the admin-server REST host -# (default :17128). The cases below walk add → get → list → change → disable → -# list-default vs. list-with-disabled to exercise the full CRUD cycle. The -# client supplies the template id on POST; subsequent steps read it back from -# /tmp/uitpl-add.json. +# Dashboard-template CRUD over the admin-server `ui-management` module, driven by +# `swctl admin ui-template ...` instead of raw curl. The cases walk +# create -> get -> update -> get -> disable -> list-default vs. list-with-disabled +# to exercise the full cycle. The client supplies a fixed template id on create so +# the later steps address it without reading it back. cases: - # 1. POST a new template — client supplies the id. + # 1. Create a new template — the client supplies the id; the configuration is + # passed as a file (swctl reads the JSON-encoded body from --file). - query: | - curl -s -X POST http://${oap_host}:${oap_17128}/ui-management/templates \ - -H 'Content-Type: application/json' \ - -d '{"id":"e2e-tpl-id","configuration":"{\"name\":\"e2e-tpl\",\"v\":1}"}' \ - | tee /tmp/uitpl-add.json \ - | yq -P '. | {"status": .status, "hasId": (.id != null and .id != "")}' + printf '%s' '{"name":"e2e-tpl","v":1}' > /tmp/uitpl-cfg.json + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template create --id e2e-tpl-id -f /tmp/uitpl-cfg.json \ + | yq -P '{"status": .status, "hasId": (.id != null and .id != "")}' expected: expected/template-write-success.yml - # 2. GET the template by the id returned from step 1. + # 2. Get the template by the fixed id. - query: | - ID=$(yq -r '.id' /tmp/uitpl-add.json); - curl -s -X GET http://${oap_host}:${oap_17128}/ui-management/templates/$ID \ - | yq -P '. | {"configuration": .configuration, "disabled": .disabled}' + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template get e2e-tpl-id \ + | yq -P '{"configuration": .configuration, "disabled": .disabled}' expected: expected/template-fetched.yml - # 3. PUT to update the configuration. Same id, new body. + # 3. Update the configuration. Same id, new body. - query: | - ID=$(yq -r '.id' /tmp/uitpl-add.json); - curl -s -X PUT http://${oap_host}:${oap_17128}/ui-management/templates \ - -H 'Content-Type: application/json' \ - -d "{\"id\":\"$ID\",\"configuration\":\"{\\\"name\\\":\\\"e2e-tpl\\\",\\\"v\\\":2}\"}" \ - | yq -P '. | {"status": .status, "id": .id}' + printf '%s' '{"name":"e2e-tpl","v":2}' > /tmp/uitpl-cfg2.json + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template update --id e2e-tpl-id -f /tmp/uitpl-cfg2.json \ + | yq -P '{"status": .status, "id": .id}' expected: expected/template-changed.yml - # 4. GET again — configuration should now reflect the PUT body. + # 4. Get again — configuration should now reflect the update. - query: | - ID=$(yq -r '.id' /tmp/uitpl-add.json); - curl -s -X GET http://${oap_host}:${oap_17128}/ui-management/templates/$ID \ - | yq -P '. | {"configuration": .configuration, "disabled": .disabled}' + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template get e2e-tpl-id \ + | yq -P '{"configuration": .configuration, "disabled": .disabled}' expected: expected/template-updated.yml - # 5. POST .../disable — soft-delete. + # 5. Disable — soft-delete. - query: | - ID=$(yq -r '.id' /tmp/uitpl-add.json); - curl -s -X POST http://${oap_host}:${oap_17128}/ui-management/templates/$ID/disable \ - | yq -P '. | {"status": .status, "id": .id}' + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template disable e2e-tpl-id \ + | yq -P '{"status": .status, "id": .id}' expected: expected/template-changed.yml # 6. Default list excludes disabled — should be empty. - query: | - curl -s -X GET http://${oap_host}:${oap_17128}/ui-management/templates \ - | yq -P '. | length' + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template list | yq -P '. | length' expected: expected/template-list-empty.yml - # 7. includingDisabled=true returns the soft-deleted entry. + # 7. --include-disabled returns the soft-deleted entry. - query: | - ID=$(yq -r '.id' /tmp/uitpl-add.json); - curl -s -X GET "http://${oap_host}:${oap_17128}/ui-management/templates?includingDisabled=true" \ - | yq -P '.[] | select(.id == "'"$ID"'") | {"disabled": .disabled}' + swctl --display json --admin-url=http://${oap_host}:${oap_17128} \ + admin ui-template list --include-disabled \ + | yq -P '.[] | select(.id == "e2e-tpl-id") | {"disabled": .disabled}' expected: expected/template-list-disabled.yml diff --git a/test/e2e-v2/script/env b/test/e2e-v2/script/env index 0944fddff9..d51720c7b9 100644 --- a/test/e2e-v2/script/env +++ b/test/e2e-v2/script/env @@ -27,7 +27,7 @@ SW_BANYANDB_COMMIT=84b919efca3fee3d51df9e97a734a9f10ae6f1d2 SW_AGENT_PHP_COMMIT=d1114e7be5d89881eec76e5b56e69ff844691e35 SW_PREDICTOR_COMMIT=54a0197654a3781a6f73ce35146c712af297c994 -SW_CTL_COMMIT=9a1beab08413ce415a00a8547a238a14691c5655 +SW_CTL_COMMIT=b447211a9319eeb29a445335e9c2536f8c1aa23d # Third-party image versions used by e2e infrastructure (not skywalking # components). Pinned here so the matrix is reproducible.
