diff --git a/adk-rust/crates/aw-contour-smoke/src/main.rs b/adk-rust/crates/aw-contour-smoke/src/main.rs index 7e25a34..d44c299 100644 --- a/adk-rust/crates/aw-contour-smoke/src/main.rs +++ b/adk-rust/crates/aw-contour-smoke/src/main.rs @@ -116,7 +116,7 @@ fn run_proxmox_remote() -> Result { check_tcp(&mut counts, "nginx http", "127.0.0.1", 80); check_tcp(&mut counts, "nginx https", "127.0.0.1", 443); check_tcp(&mut counts, "proxmox web", "127.0.0.1", 8006); - check_tcp(&mut counts, "1C company API", "192.0.2.2", 8710); + check_tcp(&mut counts, "1C company API", "10.10.10.2", 8710); check_tcp(&mut counts, "clickhouse native", "127.0.0.1", 9000); check_tcp(&mut counts, "clickhouse http", "127.0.0.1", 8123); if let Ok(out) = command_output("ss", &["-tulpn"]) { @@ -135,29 +135,26 @@ fn run_proxmox_remote() -> Result { "https://127.0.0.1/healthz", &[200], ); - check_http_redirect( + check_http_code( &mut counts, - &no_redirect_http, - "go proxmox gui", + &http, + "go proxmox gui protected", "https://127.0.0.1/go/proxmox-gui", - &[301, 302, 307, 308], - Some("https://192.0.2.2:8006/"), + &[401], ); - check_http_redirect( + check_http_code( &mut counts, - &no_redirect_http, - "go file1c brief", + &http, + "go file1c brief protected", "https://127.0.0.1/go/file1c-brief", - &[301, 302, 307, 308], - Some("http://192.0.2.2:8710/manager/brief"), + &[401], ); - check_http_redirect( + check_http_code( &mut counts, - &no_redirect_http, - "go file1c actions", + &http, + "go file1c actions protected", "https://127.0.0.1/go/file1c-actions", - &[301, 302, 307, 308], - Some("http://192.0.2.2:8710/manager/actions"), + &[401], ); section("1C Company API"); @@ -165,24 +162,24 @@ fn run_proxmox_remote() -> Result { &mut counts, &no_redirect_http, "1C root redirect", - "http://192.0.2.2:8710/", + "http://10.10.10.2:8710/", &[307], ); for (name, url) in [ - ("1C /health", "http://192.0.2.2:8710/health"), - ("1C /api/health", "http://192.0.2.2:8710/api/health"), - ("1C manager brief", "http://192.0.2.2:8710/manager/brief"), + ("1C /health", "http://10.10.10.2:8710/health"), + ("1C /api/health", "http://10.10.10.2:8710/api/health"), + ("1C manager brief", "http://10.10.10.2:8710/manager/brief"), ( "1C manager actions", - "http://192.0.2.2:8710/manager/actions", + "http://10.10.10.2:8710/manager/actions", ), ( "1C manager recovery", - "http://192.0.2.2:8710/manager/recovery", + "http://10.10.10.2:8710/manager/recovery", ), ( "1C weekly digest", - "http://192.0.2.2:8710/manager/digest/weekly", + "http://10.10.10.2:8710/manager/digest/weekly", ), ] { check_http_code(&mut counts, &http, name, url, &[200]); @@ -345,39 +342,6 @@ fn check_http_code(counts: &mut Counts, client: &Client, name: &str, url: &str, } } -fn check_http_redirect( - counts: &mut Counts, - client: &Client, - name: &str, - url: &str, - expected: &[u16], - expected_location: Option<&str>, -) { - match client.head(url).send() { - Ok(response) => { - let code = response.status().as_u16(); - let location = response - .headers() - .get(reqwest::header::LOCATION) - .and_then(|value| value.to_str().ok()) - .unwrap_or(""); - let location_ok = expected_location.is_none_or(|expected| location.contains(expected)); - if expected.contains(&code) && location_ok { - counts.pass(format!( - "{name} HTTP {code} {}", - if location.is_empty() { url } else { location } - )); - } else { - counts.fail(format!( - "{name} HTTP {code} {}", - if location.is_empty() { url } else { location } - )); - } - } - Err(err) => counts.fail(format!("{name} HTTP error {url}: {err}")), - } -} - fn check_command(counts: &mut Counts, name: &str, cmd: &str, args: &[&str]) { match command_output(cmd, args) { Ok(out) => { diff --git a/scripts/aw-contour-smoke-gateway.sh b/scripts/aw-contour-smoke-gateway.sh index eddece2..f312c2c 100644 --- a/scripts/aw-contour-smoke-gateway.sh +++ b/scripts/aw-contour-smoke-gateway.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Smoke checks for the Proxmox/gateway/1C host 192.0.2.2. +# Smoke checks for the Proxmox/gateway/1C host 10.10.10.2. set -uo pipefail @@ -193,25 +193,25 @@ section "Ports" check_tcp "nginx http" 127.0.0.1 80 check_tcp "nginx https" 127.0.0.1 443 check_tcp "proxmox web" 127.0.0.1 8006 -check_tcp "1C company API" 192.0.2.2 8710 +check_tcp "1C company API" 10.10.10.2 8710 check_tcp "clickhouse native" 127.0.0.1 9000 check_tcp "clickhouse http" 127.0.0.1 8123 ss -tulpn | grep -E ':(80|443|8006|8710|8123|9000)\b' | sed 's/^/ /' || true section "Gateway HTTP" check_http_code "nginx healthz" "https://127.0.0.1/healthz" '^200$' -check_http_redirect "go proxmox gui" "https://127.0.0.1/go/proxmox-gui" '^30[1278]$' 'https://192.0.2.2:8006/' -check_http_redirect "go file1c brief" "https://127.0.0.1/go/file1c-brief" '^30[1278]$' 'http://192.0.2.2:8710/manager/brief' -check_http_redirect "go file1c actions" "https://127.0.0.1/go/file1c-actions" '^30[1278]$' 'http://192.0.2.2:8710/manager/actions' +check_http_code "go proxmox gui protected" "https://127.0.0.1/go/proxmox-gui" '^401$' +check_http_code "go file1c brief protected" "https://127.0.0.1/go/file1c-brief" '^401$' +check_http_code "go file1c actions protected" "https://127.0.0.1/go/file1c-actions" '^401$' section "1C Company API" -check_http_code "1C root redirect" "http://192.0.2.2:8710/" '^307$' -check_http_code "1C /health" "http://192.0.2.2:8710/health" '^200$' -check_http_code "1C /api/health" "http://192.0.2.2:8710/api/health" '^200$' -check_http_code "1C manager brief" "http://192.0.2.2:8710/manager/brief" '^200$' -check_http_code "1C manager actions" "http://192.0.2.2:8710/manager/actions" '^200$' -check_http_code "1C manager recovery" "http://192.0.2.2:8710/manager/recovery" '^200$' -check_http_code "1C weekly digest" "http://192.0.2.2:8710/manager/digest/weekly" '^200$' +check_http_code "1C root redirect" "http://10.10.10.2:8710/" '^307$' +check_http_code "1C /health" "http://10.10.10.2:8710/health" '^200$' +check_http_code "1C /api/health" "http://10.10.10.2:8710/api/health" '^200$' +check_http_code "1C manager brief" "http://10.10.10.2:8710/manager/brief" '^200$' +check_http_code "1C manager actions" "http://10.10.10.2:8710/manager/actions" '^200$' +check_http_code "1C manager recovery" "http://10.10.10.2:8710/manager/recovery" '^200$' +check_http_code "1C weekly digest" "http://10.10.10.2:8710/manager/digest/weekly" '^200$' section "ClickHouse" check_docker_container "aw-rus-1c-clickhouse" diff --git a/scripts/aw-contour-smoke-local.sh b/scripts/aw-contour-smoke-local.sh index c2509d1..9cfdaae 100644 --- a/scripts/aw-contour-smoke-local.sh +++ b/scripts/aw-contour-smoke-local.sh @@ -19,24 +19,35 @@ ANSIBLE_DIR="$REPO_ROOT/ansible" INVENTORY="${AW_SMOKE_INVENTORY:-$ANSIBLE_DIR/inventory.ini}" REMOTE_SCRIPT_SRC="$REPO_ROOT/scripts/aw-contour-smoke-gateway.sh" REMOTE_SCRIPT_DST="${AW_SMOKE_REMOTE_SCRIPT:-/usr/local/sbin/aw-contour-smoke.sh}" -REMOTE_RUST_SRC="${AW_SMOKE_REMOTE_RUST_SRC:-${CARGO_TARGET_DIR:-$REPO_ROOT/adk-rust/target}/release/aw-contour-smoke}" +DEFAULT_REMOTE_RUST_SRC="" +for rust_candidate in \ + "${CARGO_TARGET_DIR:-}/release/aw-contour-smoke" \ + "$HOME/.cache/detmir-adk-rust-target/release/aw-contour-smoke" \ + "$REPO_ROOT/adk-rust/target/release/aw-contour-smoke" +do + if [ -n "$rust_candidate" ] && [ -x "$rust_candidate" ]; then + DEFAULT_REMOTE_RUST_SRC="$rust_candidate" + break + fi +done +REMOTE_RUST_SRC="${AW_SMOKE_REMOTE_RUST_SRC:-$DEFAULT_REMOTE_RUST_SRC}" REMOTE_RUST_DST="${AW_SMOKE_REMOTE_RUST_BIN:-/usr/local/sbin/aw-contour-smoke}" -AW_SERVER="${AW_SMOKE_AW_SERVER:-http://192.0.2.13:5600}" -WORKTIME_API="${AW_SMOKE_WORKTIME_API:-http://192.0.2.13:5610}" -GRAFANA_URL="${AW_SMOKE_GRAFANA_URL:-http://192.0.2.11:3000}" +AW_SERVER="${AW_SMOKE_AW_SERVER:-http://10.10.10.13:5600}" +WORKTIME_API="${AW_SMOKE_WORKTIME_API:-http://10.10.10.13:5610}" +GRAFANA_URL="${AW_SMOKE_GRAFANA_URL:-http://10.10.10.11:3000}" GRAFANA_USER="${GRAFANA_USER:-igor}" GRAFANA_PASSWORD="${GRAFANA_PASSWORD:-}" -PROXMOX_HOST="${AW_SMOKE_PROXMOX_HOST:-192.0.2.2}" -AW_HOST="${AW_SMOKE_AW_HOST:-192.0.2.13}" -GRAFANA_HOST="${AW_SMOKE_GRAFANA_HOST:-192.0.2.11}" -WINDOWS_HOST="${AW_SMOKE_WINDOWS_HOST:-198.51.100.18}" -AW_SOURCE_HOSTNAME="${AW_SMOKE_SOURCE_HOSTNAME:-HOST-EXAMPLE}" +PROXMOX_HOST="${AW_SMOKE_PROXMOX_HOST:-10.10.10.2}" +AW_HOST="${AW_SMOKE_AW_HOST:-10.10.10.13}" +GRAFANA_HOST="${AW_SMOKE_GRAFANA_HOST:-10.10.10.11}" +WINDOWS_HOST="${AW_SMOKE_WINDOWS_HOST:-192.168.100.18}" +AW_SOURCE_HOSTNAME="${AW_SMOKE_SOURCE_HOSTNAME:-SHARKON2025}" LOG_DIR="${AW_SMOKE_LOG_DIR:-$REPO_ROOT/output/smoke}" RUN_REMOTE="${AW_SMOKE_RUN_REMOTE:-1}" RUN_WINRM="${AW_SMOKE_RUN_WINRM:-1}" RUN_SERVER_SYSTEMD="${AW_SMOKE_RUN_SERVER_SYSTEMD:-1}" -NO_PROXY_REQUIRED="localhost,127.0.0.1,$PROXMOX_HOST,$AW_HOST,$GRAFANA_HOST,$WINDOWS_HOST,192.0.2.0/24,198.51.100.0/24" +NO_PROXY_REQUIRED="localhost,127.0.0.1,$PROXMOX_HOST,$AW_HOST,$GRAFANA_HOST,$WINDOWS_HOST,10.10.10.0/24,192.168.100.0/24" if [ -n "${no_proxy:-}" ]; then export no_proxy="$no_proxy,$NO_PROXY_REQUIRED" else @@ -72,10 +83,10 @@ usage() { Usage: $(basename "$0") [--skip-remote] [--skip-winrm] [--skip-server-systemd] Environment overrides: - AW_SMOKE_AW_SERVER=http://192.0.2.13:5600 - AW_SMOKE_WORKTIME_API=http://192.0.2.13:5610 - AW_SMOKE_GRAFANA_URL=http://192.0.2.11:3000 - AW_SMOKE_SOURCE_HOSTNAME=HOST-EXAMPLE + AW_SMOKE_AW_SERVER=http://10.10.10.13:5600 + AW_SMOKE_WORKTIME_API=http://10.10.10.13:5610 + AW_SMOKE_GRAFANA_URL=http://10.10.10.11:3000 + AW_SMOKE_SOURCE_HOSTNAME=SHARKON2025 AW_SMOKE_ENV_FILE=$HOME/.config/aw-contour-smoke.env AW_SMOKE_LOG_DIR=$REPO_ROOT/output/smoke GRAFANA_USER/GRAFANA_PASSWORD via env or a local env file @@ -249,19 +260,36 @@ check_grafana_influx_health() { } check_grafana_aw_main_dashboard_queries() { - local name="Grafana detmir-aw-main window/AFK panel queries" + local name="Grafana detmir-aw-main worktime panel queries" local tmp tmp="$(mktemp)" if curl -k -fsS -u "$GRAFANA_USER:$GRAFANA_PASSWORD" --connect-timeout 5 --max-time 20 "$GRAFANA_URL/api/dashboards/uid/detmir-aw-main" -o "$tmp" 2>"$tmp.err" && \ jq -e ' - def fixed: - (.targets[0].query // "") | contains("keep(columns: [\"_time\", \"_value\", \"host\"]"); - ([.dashboard.panels[] | select(.title == "Активность окон (сумма за 5 минут)" or .title == "AFK (сумма за 5 минут)") | fixed] | length == 2 and all(. == true)) + def query: (.targets[0].query // ""); + def has_panel($title; $checks): + any(.dashboard.panels[]?; .title == $title and (query as $q | all($checks[]; $q | contains(.)))); + has_panel("Активность RDP по часам"; [ + "aw_rdp_worktime_hourly", + "r.user_id !~ /\\$$/", + "r.user_id !~ /�/", + "group(columns: [\"_time\", \"user\"])", + "max(column: \"_value\")" + ]) and + has_panel("Сегодня: активность по сотрудникам"; [ + "aw_rdp_worktime_daily", + "group(columns: [\"report_date\", \"user\"])", + "max(column: \"_value\")", + "last()" + ]) and + has_panel("Все сотрудники: активное время по дням"; [ + "aw_rdp_worktime_summary_daily", + "Все сотрудники, ч" + ]) ' "$tmp" >/dev/null 2>&1; then pass "$name" else - fail "$name missing keep(_time,_value,host) after aggregateWindow" - jq -r '.dashboard.panels[]? | select(.title == "Активность окон (сумма за 5 минут)" or .title == "AFK (сумма за 5 минут)") | "\(.title): " + ((.targets[0].query // "") | gsub("\n"; " "))' "$tmp" 2>/dev/null | sed 's/^/ /' | head -20 + fail "$name missing expected DetMir worktime/dedupe query contract" + jq -r '.dashboard.panels[]? | select(.title == "Активность RDP по часам" or .title == "Сегодня: активность по сотрудникам" or .title == "Все сотрудники: активное время по дням") | "\(.title): " + ((.targets[0].query // "") | gsub("\n"; " "))' "$tmp" 2>/dev/null | sed 's/^/ /' | head -20 sed 's/^/ /' "$tmp.err" 2>/dev/null | head -20 fi rm -f "$tmp" "$tmp.err" @@ -436,7 +464,7 @@ check_bucket_freshness() { run_remote_proxmox_script() { if [ "$RUN_REMOTE" != "1" ]; then - skip "remote 192.0.2.2 smoke skipped" + skip "remote 10.10.10.2 smoke skipped" return fi if ! have ansible; then @@ -448,7 +476,7 @@ run_remote_proxmox_script() { return fi - section "Deploy Remote Script To 192.0.2.2" + section "Deploy Remote Script To 10.10.10.2" if [ -x "$REMOTE_RUST_SRC" ]; then if ANSIBLE_NOCOLOR=1 ansible proxmox -i "$INVENTORY" -m copy -a "src=$REMOTE_RUST_SRC dest=$REMOTE_RUST_DST owner=root group=root mode=0755" >/tmp/aw-smoke-copy-rust.$$ 2>&1; then pass "remote Rust smoke deployed to $REMOTE_RUST_DST" @@ -471,14 +499,14 @@ run_remote_proxmox_script() { fi rm -f /tmp/aw-smoke-copy.$$ - section "Remote 192.0.2.2 Smoke" + section "Remote 10.10.10.2 Smoke" local tmp tmp="$(mktemp)" if ANSIBLE_NOCOLOR=1 ansible proxmox -i "$INVENTORY" -m shell -a "$REMOTE_SCRIPT_DST" >"$tmp" 2>&1; then - pass "remote 192.0.2.2 smoke completed" + pass "remote 10.10.10.2 smoke completed" sed 's/^/ /' "$tmp" else - fail "remote 192.0.2.2 smoke failed" + fail "remote 10.10.10.2 smoke failed" sed 's/^/ /' "$tmp" fi rm -f "$tmp" @@ -544,8 +572,9 @@ check_http_code "worktime management html" "$WORKTIME_API/reports/worktime/manag section "Gateway And 1C HTTP" check_http_code "gateway healthz" "https://$PROXMOX_HOST/healthz" '^200$' -check_http_redirect "gateway proxmox redirect" "https://$PROXMOX_HOST/go/proxmox-gui" '^30[1278]$' "https://$PROXMOX_HOST:8006/" -check_http_redirect "gateway file1c brief redirect" "https://$PROXMOX_HOST/go/file1c-brief" '^30[1278]$' "http://$PROXMOX_HOST:8710/manager/brief" +check_http_code "gateway proxmox protected" "https://$PROXMOX_HOST/go/proxmox-gui" '^401$' +check_http_code "gateway file1c brief protected" "https://$PROXMOX_HOST/go/file1c-brief" '^401$' +check_http_code "gateway file1c actions protected" "https://$PROXMOX_HOST/go/file1c-actions" '^401$' check_http_code "1C /health" "http://$PROXMOX_HOST:8710/health" '^200$' check_http_code "1C /api/health" "http://$PROXMOX_HOST:8710/api/health" '^200$' check_http_code "1C manager brief" "http://$PROXMOX_HOST:8710/manager/brief" '^200$' @@ -582,7 +611,7 @@ if [ "$RUN_WINRM" = "1" ] && have ansible; then check_ansible_module "Windows win_ping" aw_windows win_ping check_ansible_win_shell "Windows sessions" aw_windows '$psi = [System.Diagnostics.ProcessStartInfo]::new(); $psi.FileName = "$env:SystemRoot\System32\query.exe"; $psi.Arguments = "user"; $psi.UseShellExecute = $false; $psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true; $psi.StandardOutputEncoding = [System.Text.Encoding]::GetEncoding(866); $psi.StandardErrorEncoding = [System.Text.Encoding]::GetEncoding(866); $p = [System.Diagnostics.Process]::Start($psi); $out = $p.StandardOutput.ReadToEnd(); $err = $p.StandardError.ReadToEnd(); $p.WaitForExit(); [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); $out; if ($err) { $err }; if ($out -match "USERNAME|ПОЛЬЗОВАТЕЛЬ|администратор|Администратор") { exit 0 } else { exit $p.ExitCode }' check_ansible_win_shell_warn "Windows collector processes" aw_windows '$p = Get-Process aw-watcher-afk,aw-watcher-window -ErrorAction SilentlyContinue; if ($p) { $p | Select-Object Name,Id,SessionId,StartTime | Format-Table -AutoSize } else { "no aw-watcher-afk/window process visible to this WinRM session" }' - check_ansible_win_shell "Windows ActivityWatch tasks" aw_windows 'schtasks /Query /TN "ActivityWatch Recovery" /FO LIST /V; schtasks /Query /TN "ActivityWatch Launch [HOST-EXAMPLE_Администратор]" /FO LIST /V' + check_ansible_win_shell "Windows ActivityWatch tasks" aw_windows 'schtasks /Query /TN "ActivityWatch Recovery" /FO LIST /V; schtasks /Query /TN "ActivityWatch Launch [SHARKON2025_Администратор]" /FO LIST /V' else skip "Windows WinRM checks skipped" fi