From 2931006e3c010d07f8259b1132dd1d330a40575b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Sun, 19 Jul 2026 02:28:35 +0200 Subject: [PATCH] release 1.13.0: devtesting fake-franken CHF via rusty New phase devtesting (aliases franken/fake-franken) probes taler-devtesting on rusty.taler-ops.ch: geniban + fake-incoming CHF to the exchange IBAN. Nexus bounce without reserve pub is OK for the plumbing probe. Stage host-agent can include the phase. --- README.md | 14 +++ TESTS.md | 10 ++ VERSION | 2 +- VERSIONS.md | 1 + check_devtesting.sh | 147 ++++++++++++++++++++++++ host-agent/env/mytops-stage.env.example | 35 ++++++ secrets.env.example | 9 ++ site-gen/console_to_html.py | 3 + taler-monitoring.sh | 8 +- 9 files changed, 227 insertions(+), 2 deletions(-) create mode 100755 check_devtesting.sh create mode 100644 host-agent/env/mytops-stage.env.example diff --git a/README.md b/README.md index 9693623..f035530 100644 --- a/README.md +++ b/README.md @@ -421,6 +421,20 @@ Soft mode only with `MONPAGES_REQUIRE_PUBLIC=0` (escape hatch). If pages are missing publicly: `sudo /home/hernani/koopa-caddy/apply-monitoring-live.sh` on koopa (FP: Infomaniak vhost for `/monitoring*`). +## Devtesting · fake-franken CHF (v1.13+) + +Phase **`devtesting`** (aliases: `franken`, `fake-franken`) exercises the +Taler Operations **rusty** host (`taler-devtesting` forced command): + +```bash +./taler-monitoring.sh -d stage.taler-ops.ch --currency CHF devtesting +``` + +Needs SSH to `devtesting@rusty.taler-ops.ch` (Host `taler-rusty-devtesting`). +Probes: CLI help, `geniban` (CH IBAN), `fake-incoming` CHF to exchange credit +IBAN. A nexus bounce *missing reserve public key* is **OK** for the plumbing +probe (no real wallet reserve required). + ## Version on monitoring pages (v1.3+ / sticky bar) The sticky bar shows the suite **tag** (e.g. `v1.11.0`) and links to that tag on Forgejo. diff --git a/TESTS.md b/TESTS.md index 7bde9f6..cd80328 100644 --- a/TESTS.md +++ b/TESTS.md @@ -193,3 +193,13 @@ totals: 40 OK, 1 ERROR, 2 WARN, 5 INFO `NO_COLOR=1` or `CLICOLOR=0` disables boxes/colours (ASCII `[ OK ]` + `-- group --` headers). Always use `printf --` friendly plain headers when colour is off (leading `---` is not a printf option). + + +## devtesting · fake-franken (v1.13+) + +| ID prefix | What | +|-----------|------| +| **devtesting.cli-** | SSH to rusty · `taler-devtesting` CLI | +| **devtesting.geniban-** | synthetic CH IBAN | +| **devtesting.credit-payto-** | exchange credit IBAN for nexus | +| **devtesting.fake-incoming-** | CHF IN via nexus; bounce missing reserve pub = plumbing OK | diff --git a/VERSION b/VERSION index 0eed1a2..feaae22 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.0 +1.13.0 diff --git a/VERSIONS.md b/VERSIONS.md index 6b9c2a7..abe5e61 100644 --- a/VERSIONS.md +++ b/VERSIONS.md @@ -17,6 +17,7 @@ Git tags: `vMAJOR.FEATURE.FIX` (e.g. `v1.8.0`). File `VERSION` omits the `v` pre | Tag | Date (UTC) | Notes | |-----|------------|--------| +| **v1.13.0** | 2026-07-19 | **Feature:** phase **devtesting** / **franken** — fake-franken CHF via `rusty.taler-ops.ch` (`taler-devtesting` geniban + fake-incoming); stage host-agent can include it. | | **v1.12.0** | 2026-07-19 | **Feature:** collapsible **monitoring env context** (agent header: env_file, suite pin, domain/hosts/flags) — `
` default closed; summary shows agent · domain · suite. | | **v1.11.0** | 2026-07-19 | **Feature:** sticky **error/warn filter** — click counts to show only error or only warn log lines; sticky bar + overviews stay; `#filter-error` / `#filter-warn` (+ `#first-*` still works). | | **v1.10.11** | 2026-07-19 | **Bugfix:** surface/aptdeploy wrappers **force** MON_HOSTS+PHASES+MONPAGES_INVENTORY (env cannot expand job to bank/exchange); wrap more mon knobs. | diff --git a/check_devtesting.sh b/check_devtesting.sh new file mode 100755 index 0000000..575138c --- /dev/null +++ b/check_devtesting.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# check_devtesting.sh — fake-franken (CHF) via rusty.taler-ops.ch taler-devtesting +# +# Requires SSH to Host DEVTESTING_SSH (default: taler-rusty-devtesting → +# rusty.taler-ops.ch user devtesting, forced command taler-devtesting). +# +# Probes: +# 1) CLI reachable (fake-incoming --help) +# 2) geniban → Swiss IBAN +# 3) fake-incoming CHF amount to exchange credit IBAN (nexus IN) +# Bounce "missing reserve public key" = plumbing OK (no real wallet reserve) +# Full credit needs a real reserve pub as --subject (optional later) +# +# Env: +# DEVTESTING_SSH default taler-rusty-devtesting (or user@host) +# DEVTESTING_AMOUNT default CHF:1.00 +# DEVTESTING_CREDIT_PAYTO exchange IBAN payto (auto from EXCHANGE_PUBLIC/keys if empty) +# DEVTESTING_EXCHANGE_IBAN fallback CH6808573105529100001 (TOPS CHF exchange) +# DEVTESTING_SKIP=1 skip phase +# SKIP_SSH=1 skip (same as other SSH phases) +# +set -euo pipefail +ROOT=$(cd "$(dirname "$0")" && pwd) +# shellcheck source=lib.sh +source "$ROOT/lib.sh" + +set_area devtesting +section "devtesting · fake-franken CHF (rusty taler-devtesting)" + +# Do NOT honor SKIP_SSH here: stage/tops profiles set SKIP_SSH=1 for container +# inside checks, but rusty.taler-ops.ch is a separate SSH hop (devtesting key). +if [ "${DEVTESTING_SKIP:-0}" = "1" ]; then + info "skip" "DEVTESTING_SKIP=1" + exit 0 +fi + +: "${DEVTESTING_SSH:=taler-rusty-devtesting}" +: "${DEVTESTING_AMOUNT:=CHF:1.00}" +: "${DEVTESTING_EXCHANGE_IBAN:=CH6808573105529100001}" +: "${SSH_CONNECT_TIMEOUT:=8}" +: "${SSH_CMD_TIMEOUT:=45}" + +ssh_dt() { + # Prefer Host alias; fall back to explicit user@host if config missing + local target="$DEVTESTING_SSH" + if ! ssh -G "$target" >/dev/null 2>&1; then + target="devtesting@rusty.taler-ops.ch" + fi + if command -v timeout >/dev/null 2>&1; then + timeout "${SSH_CMD_TIMEOUT}" ssh \ + -o BatchMode=yes \ + -o ConnectTimeout="${SSH_CONNECT_TIMEOUT}" \ + -o StrictHostKeyChecking=accept-new \ + "$target" "$@" + else + ssh \ + -o BatchMode=yes \ + -o ConnectTimeout="${SSH_CONNECT_TIMEOUT}" \ + -o StrictHostKeyChecking=accept-new \ + "$target" "$@" + fi +} + +set_group cli +info "ssh" "DEVTESTING_SSH=${DEVTESTING_SSH} amount=${DEVTESTING_AMOUNT}" + +help_out=$(ssh_dt fake-incoming --help 2>&1) || true +if printf '%s' "$help_out" | grep -q 'fake-incoming'; then + ok "cli" "taler-devtesting fake-incoming reachable via ${DEVTESTING_SSH}" +else + fail "cli" "cannot run taler-devtesting on ${DEVTESTING_SSH}" \ + "$(printf '%s' "$help_out" | tr '\n' ' ' | head -c 200)" + exit 1 +fi + +set_group geniban +iban_out=$(ssh_dt geniban 2>&1) || true +iban=$(printf '%s\n' "$iban_out" | grep -E '^CH[0-9A-Z]+$' | tail -1 || true) +if [ -n "$iban" ]; then + ok "geniban" "synthetic IBAN $iban" +else + fail "geniban" "expected CH… IBAN" "$(printf '%s' "$iban_out" | tr '\n' ' ' | head -c 160)" + exit 1 +fi + +# Credit payto: rusty libeufin-nexus only accepts the *production* exchange IBAN +# configured there (DEVTESTING_EXCHANGE_IBAN). Do not use stage /keys first-account. +set_group credit-payto +credit_payto="${DEVTESTING_CREDIT_PAYTO:-}" +if [ -z "$credit_payto" ]; then + credit_payto="payto://iban/${DEVTESTING_EXCHANGE_IBAN}?receiver-name=Taler+Operations+AG" +fi +ok "credit-payto" "$credit_payto" + +# Subject: not a real reserve — nexus should bounce missing reserve public key +# (proves CHF fake-incoming path). Real reserve pub would credit wirewatch. +subject="mon-fake-franken-$(date -u +%Y%m%d%H%M%S)-$$" +debit_payto="payto://iban/${iban}?receiver-name=MonDevTest" + +set_group fake-incoming +info "fake-incoming" "amount=${DEVTESTING_AMOUNT} subject=${subject}" +fi_rc=0 +fi_out=$(ssh_dt fake-incoming \ + --amount "${DEVTESTING_AMOUNT}" \ + --subject "${subject}" \ + --credit-payto "${credit_payto}" \ + --debit-payto "${debit_payto}" 2>&1) || fi_rc=$? + +# Normalize multi-line for matching +fi_flat=$(printf '%s' "$fi_out" | tr '\n' ' ') + +if printf '%s' "$fi_flat" | grep -qiE 'Permission denied|Connection refused|Could not resolve|No such command'; then + fail "fake-incoming" "SSH/CLI failure" "$(printf '%s' "$fi_flat" | head -c 200)" + exit 1 +fi + +if printf '%s' "$fi_flat" | grep -qiE 'Creditor must be the exchange'; then + fail "fake-incoming" "wrong credit IBAN (not exchange)" "$(printf '%s' "$fi_flat" | head -c 220)" + exit 1 +fi + +if printf '%s' "$fi_flat" | grep -qiE 'Missing amount|unable to read secrets|CalledProcessError'; then + # secrets warn alone is soft if we still see IN line + if ! printf '%s' "$fi_flat" | grep -qE 'libeufin-nexus - IN .* CHF'; then + fail "fake-incoming" "nexus rejected request" "$(printf '%s' "$fi_flat" | head -c 220)" + exit 1 + fi +fi + +if printf '%s' "$fi_flat" | grep -qE 'libeufin-nexus - IN .* CHF:[0-9]'; then + if printf '%s' "$fi_flat" | grep -qiE 'bounced.*missing reserve public key'; then + ok "fake-incoming" "CHF IN accepted by nexus · bounced (no reserve pub) — fake-franken plumbing OK" + info "note" "use real reserve pub as --subject for wirewatch credit (not required for mon probe)" + elif printf '%s' "$fi_flat" | grep -qiE 'bounced'; then + warn "fake-incoming" "CHF IN bounced (other reason)" "$(printf '%s' "$fi_flat" | grep -oiE 'bounced[^ ]* [^ ]*' | head -c 160)" + else + ok "fake-incoming" "CHF IN recorded by nexus (no bounce line)" + fi +elif [ "$fi_rc" -eq 0 ] && printf '%s' "$fi_flat" | grep -qi 'Faking incoming CHF'; then + ok "fake-incoming" "CLI completed CHF fake-incoming" +else + fail "fake-incoming" "no CHF IN line from nexus" "rc=${fi_rc} $(printf '%s' "$fi_flat" | head -c 200)" + exit 1 +fi + +info "done" "devtesting fake-franken probe finished" +exit 0 diff --git a/host-agent/env/mytops-stage.env.example b/host-agent/env/mytops-stage.env.example new file mode 100644 index 0000000..5db18bf --- /dev/null +++ b/host-agent/env/mytops-stage.env.example @@ -0,0 +1,35 @@ +# Copy to: ~/.config/taler-monitoring/env (user taler-monitoring @ betel) +# mytops CHF stage — NOT lifeline / production +# +SUITE_GIT_URL=https://git.hacktivism.ch/hernani/taler-monitoring.git +SUITE_GIT_REF=main +SUITE_DIR=$HOME/src/taler-monitoring +TALER_DOMAIN=stage.taler-ops.ch +MON_HOSTS="stage.my.taler-ops.ch stage.taler-ops.ch" +HTML_OUT=$HOME/monitoring-sites-staging +DEPLOY_WWW_ROOT=/var/www/monitoring-sites +HTML_URL_OK=/monitoring/ +HTML_URL_ERR=/monitoring_err/ +HTML_OK_DIR=monitoring +HTML_ERR_DIR=monitoring_err +# urls + monpages + fake-franken CHF (rusty.taler-ops.ch) +PHASES="urls monpages devtesting" +SKIP_SSH=1 +LOCAL_STACK=0 +INSIDE_PODMAN=0 +CONTINUE_ON_ERROR=1 +RUN_TIMEOUT=600 +STRICT_EXIT=1 +MONPAGES_REQUIRE_PUBLIC=1 +MONPAGES_INVENTORY=job +TALER_MON_LANG=en +AGENT_LABEL=mytops-stage-host-agent +STATE_NAME=taler-monitoring-mytops-stage +SUITE_UPDATE_STRICT=1 +CHECK_BANK=0 +MERCHANT_REQUIRED=1 +CHECK_LANDING=0 +# rusty devtesting (SSH Host taler-rusty-devtesting → devtesting@rusty.taler-ops.ch) +DEVTESTING_SSH=taler-rusty-devtesting +DEVTESTING_AMOUNT=CHF:1.00 +# DEVTESTING_SKIP=1 diff --git a/secrets.env.example b/secrets.env.example index 3ce731d..9b3ce12 100644 --- a/secrets.env.example +++ b/secrets.env.example @@ -69,6 +69,15 @@ # AUTH401_ADMIN_USER=admin # FRANCPAYSAN_SECRETS=$HOME/francpaysan-secrets +# --- Devtesting / fake-franken CHF (rusty.taler-ops.ch) --- +# Phase: devtesting | franken | fake-franken +# SSH Host taler-rusty-devtesting (user devtesting, forced taler-devtesting CLI). +# DEVTESTING_SSH=taler-rusty-devtesting +# DEVTESTING_AMOUNT=CHF:1.00 +# DEVTESTING_CREDIT_PAYTO= # optional; auto from EXCHANGE_PUBLIC/keys or TOPS IBAN +# DEVTESTING_EXCHANGE_IBAN=CH6808573105529100001 +# DEVTESTING_SKIP=1 # skip phase + # --- Ladder (GOA withdraw ladder · explorer pool) --- # Prefer SECRETS_ROOT / KOOPA_ADMIN_SECRETS (bank-explorer-password.txt). # Overrides only if needed: diff --git a/site-gen/console_to_html.py b/site-gen/console_to_html.py index 79d0617..1860b4f 100755 --- a/site-gen/console_to_html.py +++ b/site-gen/console_to_html.py @@ -92,6 +92,7 @@ def ui(lang: str, key: str, **kwargs) -> str: "stage_i3": "Phases: {phases} · suite auto-upgrade each timer run", "stage_i4": "Not GOA: no surface/aptdeploy/mail pages · no koopa-admin-log", "stage_i5": "Timer: taler-monitoring-mytops-stage.timer (4h) · user taler-monitoring", + "stage_i6": "devtesting: fake-franken CHF via rusty.taler-ops.ch (geniban + fake-incoming)", "pages_section_lead": "Monitoring pages themselves (suite):", "mm_title": "Mattermost chat health", "mm_summary": "mattermost.taler.net · SPA + /api/v4/system/ping · TLS", @@ -182,6 +183,7 @@ def ui(lang: str, key: str, **kwargs) -> str: "stage_i3": "Phases : {phases} · auto-upgrade suite à chaque minuterie", "stage_i4": "Pas GOA : pas de pages surface/aptdeploy/mail · pas de koopa-admin-log", "stage_i5": "Minuterie : taler-monitoring-mytops-stage.timer (4 h) · user taler-monitoring", + "stage_i6": "devtesting : fake-franken CHF via rusty.taler-ops.ch (geniban + fake-incoming)", "pages_i6": "Hôte de cette page : {host} · libellé : {label}", "pages_section_lead": "Les pages de monitoring elles-mêmes (suite) :", "mm_title": "Santé du chat Mattermost", @@ -612,6 +614,7 @@ def monitoring_scope( ui(lang, "stage_i3", phases=phase_txt), ui(lang, "stage_i4"), ui(lang, "stage_i5"), + ui(lang, "stage_i6"), ui(lang, "pages_i6", host=host, label=label), ] return { diff --git a/taler-monitoring.sh b/taler-monitoring.sh index e3e342b..679049d 100755 --- a/taler-monitoring.sh +++ b/taler-monitoring.sh @@ -104,6 +104,9 @@ Phases: mail MX/SMTP/IMAP for Taler mail (firefly, pixel/TSA, catalogued domains) monpages public monitoring HTML via FQDN (obligatory ERROR; GOA full inventory, FP only FP) uses MON_HOSTS + HTML_URL_OK (same as host-agent); v1.3.1+ + devtesting fake-franken CHF via rusty.taler-ops.ch (taler-devtesting) + geniban + fake-incoming; needs SSH Host DEVTESTING_SSH (key to devtesting@rusty) + franken|fake-franken aliases for devtesting all urls + inside + versions + sanity + e2e (SSH phases only on koopa) full all + server + ladder + auth401 (maximum; long-running, needs secrets) NOTE: surface is never included in all/full — pass it explicitly @@ -245,7 +248,7 @@ while [ $# -gt 0 ]; do CURRENCY_OVERRIDE="$2"; shift 2 ;; --no-probe) NO_PROBE=1; shift ;; - urls|inside|versions|sanity|server|e2e|ladder|goa-ladder|auth401|aptdeploy|apt-deploy|apt_src|surface|ecosystem|mattermost|mail|monpages|pages|all|full) PHASES+=("$1"); shift ;; + urls|inside|versions|sanity|server|e2e|ladder|goa-ladder|auth401|aptdeploy|apt-deploy|apt_src|surface|ecosystem|mattermost|mail|monpages|pages|devtesting|franken|fake-franken|fake_franken|all|full) PHASES+=("$1"); shift ;; *) # bare domain shorthand: ./taler-monitoring.sh taler.net if [[ "$1" == *.* && "$1" != *://* && "$1" != -* ]]; then @@ -532,6 +535,9 @@ for p in "${PHASES[@]}"; do mattermost) run_phase mattermost "$ROOT/check_mattermost.sh" || ec=1 ;; mail) run_phase mail "$ROOT/check_mail.sh" || ec=1 ;; monpages|pages) run_phase monpages "$ROOT/check_monitoring_pages.sh" || ec=1 ;; + devtesting|franken|fake-franken|fake_franken) + run_phase devtesting "$ROOT/check_devtesting.sh" || ec=1 + ;; esac done