Skip to content

fix(core): watcher block dotted-path literals → real substitutions + sanitize coverage (#172) - #175

Draft
fupelaqu wants to merge 3 commits into
feature/REPL.2from
fix/172-watcher-substitutions
Draft

fix(core): watcher block dotted-path literals → real substitutions + sanitize coverage (#172)#175
fupelaqu wants to merge 3 commits into
feature/REPL.2from
fix/172-watcher-substitutions

Conversation

@fupelaqu

Copy link
Copy Markdown
Contributor

Closes #172. Refs #169 (0.20.2 line). STACKED ON #165 (feature/REPL.2) — merge after it; GitHub will retarget to main when #165 merges.

Root cause

In core/src/main/resources/softnetwork-elastic.conf, the elastic.watcher block's five credential-ish keys were literal strings, not HOCON substitutions:

method = elastic.credentials.method        # a STRING, not ${elastic.credentials.method}
username = elastic.credentials.username
password = elastic.credentials.password
api-key = elastic.credentials.api-key
bearer-token = elastic.credentials.bearer-token

Consequences:

  • the watcher block never inherited the main credentials;
  • watcher.api-key resolved to the non-empty garbage string "elastic.credentials.api-key", which ElasticAuthMethod.fromCredentials auto-detected as ApiKeyAuth (apiKey.exists(_.nonEmpty));
  • the elastic.watcher.* paths sat entirely outside the sanitize / empty-env-≡-unset treatment REPL.2 (REPL.2 — Connection config precedence: CLI flag > env var > config file > default #165) added for elastic.credentials.*.

Fix

softnetwork-elastic.conf — every watcher key now inherits the resolved elastic.credentials value via a real substitution, with the ${?ELASTIC_WATCHER_*} per-key env overrides preserved:

  • method = ${?elastic.credentials.method}optional substitution: after REPL.2 that key is absent unless ELASTIC_AUTH_METHOD is set (auth auto-detect design); a required substitution would fail resolution.
  • username / password / api-key / bearer-token = ${elastic.credentials.<key>}required substitutions, safe because the post-REPL.2 credentials block still carries "" defaults for all four (verified in the worktree conf), so the keys are always present.

Decision — scheme/host/port inherit too (${elastic.credentials.scheme|host|port} instead of their own http/localhost/9200 literals). Rationale: the watcher targets the same cluster as the main connection (principle of least surprise); grep-verified that nothing in the repo constructs a client from ElasticConfig.watcher today, so this is behavior-safe — and with nothing set the resolved values are byte-identical to the old literals. ELASTIC_WATCHER_SCHEME/HOST/PORT still override per key for a genuinely separate watcher cluster. Known (pre-existing) limitation: substitutions resolve inside the builtin layer, so watcher inheritance follows defaults and ELASTIC_* env vars, not CLI flags / user-file credential overrides — documented, unchanged from the credentials semantics REPL.2 shipped.

CliConfig — REPL.2's mechanisms extended to the watcher paths:

  • envLayer now covers the ELASTIC_WATCHER_* family with the exact same rules: empty/whitespace-only env ≡ unset; scheme/host/port values trimmed; credential values verbatim. ELASTIC_WATCHER_AUTH_METHOD deliberately stays out of this layer — parity with ELASTIC_AUTH_METHOD, which only enters through the builtin tail.
  • sanitize (refactored into a repairEmpty helper, generalized per path list): an empty/missing watcher scheme/host/port now repairs to the already-repaired main connection coordinate (inherit semantics) instead of being left broken.

Test evidence

sbt "core/testOnly *CliConfigSpec"18/18 green (12 pre-existing REPL.2 matrix tests untouched + 6 new):

  1. nothing set → watcher inherits the resolved credentials values; watcher.method absent; no key carries the old dotted-path garbage;
  2. custom credentials values propagate through the builtin conf substitutions (parseResources + override + resolve — same mechanics as ELASTIC_CREDENTIALS_* at load time);
  3. ELASTIC_WATCHER_* env overrides beat inherited and file values (trim/verbatim rules asserted);
  4. empty ELASTIC_WATCHER_HOST/_PORT behave as unset (file value wins; inherited default survives);
  5. sanitize repairs an empty watcher coordinate from a config file to the main coordinate;
  6. regression: ElasticConfig(...).watcher.authMethod == None with nothing set (was ApiKeyAuth from the garbage string).

Also green: sbt "+ core/compile" (2.12.20 + 2.13.16), sbt scalafmtAll, sbt headerCheck. Pure JVM, no Docker; env and config-file layers injected through REPL.2's two-parameter buildElasticConfig seam (no config.file sysprop mutation).

Note: install.sh needs no change — REPL.2's launcher empty-env unset loop already covers the ELASTIC_WATCHER_* family.

🤖 Generated with Claude Code

… coverage (Closed Issue #172)

The elastic.watcher block's credential keys were literal strings
(method = elastic.credentials.method etc.), so the watcher never
inherited the main credentials and auth auto-detection picked the
garbage string "elastic.credentials.api-key" as ApiKeyAuth.

- softnetwork-elastic.conf: all watcher keys now inherit the resolved
  elastic.credentials value via real substitutions (optional
  ${?elastic.credentials.method} — that key is absent by design after
  REPL.2); scheme/host/port inherit too (watcher targets the main
  cluster by default); ${?ELASTIC_WATCHER_*} overrides preserved.
- CliConfig.envLayer: ELASTIC_WATCHER_* family gets the same
  empty-env-is-unset treatment as the main family (coordinates trimmed,
  credential values verbatim).
- CliConfig.sanitize: empty/missing watcher scheme/host/port repair to
  the (already repaired) main connection coordinates.
- CliConfigSpec: 6 new pure-JVM tests (inheritance incl. custom values
  through the builtin substitutions, env override, empty-env-is-unset,
  sanitize repair, garbage-ApiKeyAuth regression).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant