ETM Connector Runbook (iter-1)

Operational playbook for the Phase 1-a ETM ingestion module. Covers the pre-flight egress check, session cache invalidation, and the iter-1 rate-bucket defaults.

EgressPolicy.FixedIPRequired=true

The real ETM API whitelists inbound IPs. Before starting supplier-sync in prod, verify the pod egresses through an IP registered with ETM operations. Iter-1 does not enforce this automatically; Phase 5 adds an admission gate that blocks startup on misconfig.

Manual pre-flight check

kubectl exec -it supplier-sync-<pod> -- curl -s https://api.ipify.org
# must return the whitelisted IP

If the IP drifts, open a ticket with ETM ops before restarting the pod (a 403-burst from non-whitelisted IPs cascades into AuthRejected counts and eventually trips the MarkFailing circuit — ADR-0024 §4).

Session cache

Redis key tracium:etm:session:<credential_ref> with TTL equal to the SUPPLIER_ETM_SESSION_TTL env (default 2h). Clear manually if an ETM session is stuck:

redis-cli -n 0 DEL tracium:etm:session:etm-system

The SessionManager transparently falls back to an in-memory cache when Redis is unavailable; dev stacks do not require Redis to exercise the ETM loop.

Rate buckets

Iter-1 defaults (override via RATELIMIT_* env):

  • AuthBucket: 1 req / 120s (RATELIMIT_AUTH_RPS=0.008333, RATELIMIT_AUTH_BURST=1). Used on POST /user/login only.
  • DataBucket[goods|price|remains|async_poll]: 1 req/s shared across classes in iter-1. Phase 2 splits into per-class buckets once per-endpoint configs move into the DB-backed supplier profile.

See platform/ratebuckets/README.md for the token-bucket semantics and Redis key layout.

Environment variables

VariableDefaultPurpose
SUPPLIER_ETM_BASE_URL(empty — module disabled)ETM API root: http://mock-etm:9000, https://itest2.etm.ru/api/v1, or https://ipro.etm.ru/api/v1
SUPPLIER_ETM_LOGINAccount login
SUPPLIER_ETM_PASSWORDAccount password
SUPPLIER_ETM_SESSION_TTL2hSession cache TTL
SUPPLIER_ETM_SEED_FILEbackend/config/etm-seed.yamlIter-1 seed enumerator YAML

Empty SUPPLIER_ETM_BASE_URL disables the entire ETM module — ingestion/infra/etm/di.go providers return nil and the orchestrator fails loudly on first use (operator misconfig).

Special SKUs (mock-ETM)

deploy/docker/resources/mock-etm/ ships deterministic fixtures for the docker-compose test profile:

  • ABC123 — happy path, returns full goods/price/remains fixtures.
  • NOTFOUND — 404 on every endpoint; exercises the ErrNotFound branch.
  • RATELIMITED — 429 with Retry-After: 60 on /price; exercises ErrRateLimited.
  • type=etm&session-id=mock-session — required on data-plane calls (other values → 403 for SessionExpired coverage).

Bring up the mock:

cd deploy/docker
docker compose --profile test up -d mock-etm

Manual smoke against ETM test/prod

Provider-contract suite includes a gated live-smoke. Default target is itest2; override TRACIUM_ETM_BASE_URL when prod parity is required.

cd backend
TRACIUM_ETM_REAL=1 \
TRACIUM_ETM_BASE_URL=https://itest2.etm.ru/api/v1 \
TRACIUM_ETM_LOGIN=<login> \
TRACIUM_ETM_PASSWORD=<password> \
TRACIUM_ETM_SAMPLE_SKU=9536092 \
go test -tags=provider_contract ./test/provider-contract -run TestETMContract_RealServiceSmoke -count=1