Skip to content

fix(arkime): un-shadow db.pl, and make #3346's translation actually run (#3343) - #3392

Merged
Xore merged 4 commits into
mainfrom
oc/3343-arkime-index-templates
Sep 27, 2026
Merged

Xore merged 4 commits into
mainfrom
oc/3343-arkime-index-templates

Conversation

@Xore

@Xore Xore commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Note on issue state

This issue is already closed (as fixed by #3346), so this PR is a judgement call I would like reviewed rather than assumed: the subject of the issue -- Arkime's own index templates never apply -- is still literally true in the deployed stack, because #3346's translation step never ran. The residual db.pl refusal was also left open in a comment on the issue. Happy to close this if you read the scope differently.

The finding

arkime-init's command: is a YAML > folded block scalar. A folded scalar joins every run of adjacent non-empty lines with a single space. #3346 put the node invocation directly under its comment with no blank line between, so the whole thing rendered as one line:

fi # #3343: translate ... See arkime/composable-templates.js. /opt/arkime/bin/node /opt/arkime/composable-templates.js

The invocation is entirely inside the comment. composable-templates.js had never once run.

Nothing caught it: bash -n accepts the folded line either way, the YAML reads as correct, and there was no test over the rendering. The other 90 lines of that command fold harmlessly (the shell bodies are more indented, so they stay literal).

What this changes

composable-templates.js — a shadow subcommand, run before db.pl and restored after. It deletes every composable template covering an Arkime index family, stashing each body first, so db.pl has nothing to collide with. This is the residual refusal from the issue's comment, implemented as a variant of the option offered there.

Coverage is decided by Elasticsearch's simpleMatch glob semantics, not by name, so a renamed or second catch-all is covered too. Worth noting how the obvious implementation is wrong: testing one invented probe name (arkime_sessions3-shadow-probe) misses arkime_sessions3-2* and arkime_history_v1-?, both of which overlap db.pl's patterns just as hard as a bare *. The matcher asks whether a pattern can match some index of the family.

compose.yml — a bounded three-attempt retry around db.pl. This covers the one race a shadow cannot: a template created while db.pl runs, by elasticsearch-setup's catch-all landing mid-run. It exits non-zero without writing the done marker rather than wedging the deploy, since arkime-capture and arkime-viewer both block on it.

elasticsearch-setup.sh — the false claim removed. The comment asserted the catch-all "explicitly excluded" arkime_sessions3-*/arkime_history_v1-*. Composable templates have no exclusion syntax, so those entries were literal index names matching nothing. The inert entries are gone and the comment names the mechanism that actually works. The 60s wait is kept -- it is the common case and needs no shadow/restore cycle -- but is no longer load-bearing.

Verification

tests/docs/test_3343_fix.py, 22 tests:

  • the folded-scalar renderer, cross-checked against PyYAML, asserting the commands survive as commands and that no command line is swallowed by a comment;
  • docker compose config-clean, bash -n and shellcheck --severity=error clean;
  • the shadow/restore cycle driven end to end against a stub Elasticsearch over HTTP: only Arkime-covering templates are deleted, every other family (dionaea-*, ml-anomalies, ...) survives, stashed bodies come back, the generated templates and the ip-fix fragment are not resurrected, the stash ends empty, and the catch-all is restored even when generate() throws;
  • the merge result asserted against the issue's own table: firstPacket date, node keyword, source.ip/destination.ip typed ip, number_of_replicas: 0, and dynamic_templates in the order *Ip, *Tokens, strings_as_keyword -- reversed, every IP is a keyword again.

python -m pytest tests/docs/ -v → 469 passed, 1 xfailed. Against main the same file fails 15 of them, including the headline fold test.

Not done here

  • arkime-capture runs db.pl upgradenoprompt --ifneeded after the marker. That is a template update with unchanged patterns, which Elasticsearch only warns about, so it is not the create-path failure above -- but it is not shadowed either, and a future Arkime bump that changes a legacy pattern would surface there.
  • Existing indices keep their pre-fix mappings; mappings are not retroactive. Remapping the historical sessions indices is a separate migration, as the issue's closing comment already notes.

Refs #3343

@github-actions

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

Comment thread arcane/home/honeypot-init/arkime/composable-templates.js Fixed
…3343)

into composable ones, and left the refusal it could not get past open:
Elasticsearch refuses to create a legacy template whose patterns match an
existing composable one, and single-node-replica-default matches "*", so
db.pl could never install or refresh any of Arkime's templates again. On an
Arkime version bump that needed a template upgrade, arkime-init's db.pl would
fail under `set -e`, the done-marker would not be written, and capture and
viewer would wait on it forever.

It was worse than that. arkime-init's command is a YAML `>` folded block
scalar, and a folded scalar joins every run of adjacent non-empty lines with
a single space. The node invocation sat directly under its comment with no
blank line between, so the whole thing rendered as one `fi` line with a
trailing comment:

  fi # #3343: translate ... See arkime/composable-templates.js. /opt/arkime/bin/node /opt/arkime/composable-templates.js

composable-templates.js had therefore never once run, and #3346's fix was
inert while still reading as correct in the YAML. bash -n accepts the
result either way and nothing else exercised the rendering.

composable-templates.js gains a `shadow` subcommand, run before db.pl and
restored after: it deletes every composable template that covers an Arkime
index family, stashing each body first, so db.pl has nothing to collide
with. Coverage is decided by Elasticsearch's own simpleMatch glob semantics
rather than by name, so a renamed or second catch-all is shadowed too --
the previous approach of testing one invented probe name missed even
arkime_sessions3-2*, which overlaps arkime_sessions3-* just as hard. The
restore runs in a `finally`, because the shadowed set includes the
replicas catch-all for every template-less index family in the stack and
leaving it deleted would turn one failure into a yellow cluster.

compose.yml wraps db.pl in a bounded three-attempt retry, which covers the
one race a shadow cannot: a template created *while* db.pl runs, by
elasticsearch-setup's catch-all landing mid-run. Both db.pl verbs are safe
to re-run, and the loop exits non-zero without writing the done marker
rather than wedging the deploy permanently.

elasticsearch-setup.sh's comment claimed the catch-all "explicitly
excluded" Arkime, which was never true -- composable templates have no
exclusion syntax, so the "-arkime_..." entries were literal index names
matching nothing. The inert entries are gone and the comment now names the
mechanism that actually works. Its 60s wait for Arkime's legacy template
is kept: it is the common case and needs no shadow/restore cycle, but it
is no longer load-bearing.

Refs #3343
CodeQL flagged js/insecure-temp-file on the shadow file: it was written to a
fixed path directly in /tmp, so anything on the host could have pre-created
it as a symlink or read the stash. The stash holds the full body of every
Arkime template this script deletes from the cluster, so that is worth
closing.

The path stays fixed because the shadow and generate passes are separate
processes that must share it -- a fresh mkdtemp per process would hand each
one its own empty stash and the generate pass would have nothing to restore.
The fix is the permissions instead: the parent directory is created 0700 and
the file 0600, and neither is widened on rewrite. os.tmpdir() honours TMPDIR
and SHADOW_FILE still overrides the location outright.

Asserted in tests/docs/test_3343_fix.py so a later change cannot quietly drop
the modes back to the mkdir/write defaults.
Conflict resolution follow-up for the rebase onto main. #3391 (the #3283
retention work) landed in composable-templates.js between this branch being
cut and the rebase, and it widened the contract generate() has with
Elasticsearch: it now installs _ilm/policy/arkime-sessions-30d immediately
before the template that names it, verifies index.lifecycle.name through
_simulate_index, and adopts the arkime_sessions3-* indices already on disk.

The #3343 suite drives the real script against a stub Elasticsearch, so two
of its tests failed on the rebase with an AssertionError naming an
"unexpected" PUT /_ilm/policy/arkime-sessions-30d. No assertion changed --
the stub is what was stale, and it is what had to learn the new routes.

Its _simulate_index answer changes from a canned
{"number_of_replicas": "0"} to a composition of the templates the script
really PUT, which is what Elasticsearch does and what the script's own
post-install verification depends on. That is strictly stronger than what it
replaces: the canned reply reported 0 replicas whatever the script
installed, so a generated template that dropped number_of_replicas would
still have passed. Composed, it fails. Dropping the retention line from
build() now fails 18 tests across both suites rather than passing silently.

The adoption routes answer with an empty index list, so the #3343
assertions stay about templates and the shadow; the adoption contract
itself remains asserted in tests/docs/test_3283_fix.py, which owns it.

Refs #3343, #3283
@Xore
Xore force-pushed the oc/3343-arkime-index-templates branch from 4ab43c9 to 7a80c9c Compare September 27, 2026 10:18
… ordering

Follow-up to 7a80c9c, which taught this stub the routes #3283 added. That
commit answered them, but answered _cat/indices with an empty list and
_settings with a constant, so the adoption branch was never actually entered
from this suite -- the routes were registered and dead. tests/docs/
test_3283_fix.py owns the adoption contract and pins it properly; this change
is about the *combined* run, which is what this suite owns.

The stub now models the one field the script reads: a name maps to whatever
index.lifecycle.name it carries, or None for unmanaged. _stubbed() seeds one
unmanaged sessions index, so the functional tests drive the real path, and
put_ilm_policies records the body so the ordering can be asserted rather than
assumed.

New test: the run that restores the catch-all is the same run that installs
arkime-sessions-30d and adopts what predates it, and the policy is installed
before the template that names it. That ordering is the part that breaks
silently -- an index template naming an ILM policy that does not exist yet
fails index creation outright, and Elasticsearch validates it at index
creation rather than here, so nothing upstream would report it.

Non-vacuous, checked by mutating the script and reverting:
  * moving ensurePolicy after the template PUT fails the new test
    ("the policy was installed after the template naming it")
  * deleting the adoption loop fails it
    ("arkime_sessions3-2026.09.01 was not adopted onto the policy: None")

Local: pytest tests/docs/ 521 passed, 1 xfailed (main: 497 passed, same
xfail); test_3343 + test_3283 together 46 passed. scripts/tests unchanged
from main: the same 2 pre-existing test_compose_drift_watch_sweep failures
that fail on a pristine main checkout on this host.
@Xore
Xore merged commit fffc85b into main Sep 27, 2026
114 of 115 checks passed
@Xore
Xore deleted the oc/3343-arkime-index-templates branch September 27, 2026 11:26
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.

2 participants