Skip to content

fix(inventory): own writes, overlays, and lookup names consistently - #32

Merged
ntwrknrd merged 2 commits into
mainfrom
fix/inventory-write-target
Oct 28, 2026
Merged

ntwrknrd merged 2 commits into
mainfrom
fix/inventory-write-target

Conversation

@ntwrknrd

@ntwrknrd ntwrknrd commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

Inventory edits made through nssh kept getting written into the root config.yaml instead of the included provider files, and aliases shared by two hosts resolved to a random host. A host defined locally and also cached from NetBox could also lose its local settings, and inv list showed overridden NetBox hosts twice. This PR fixes all four.

Implementation Notes

Choosing the write file

  • InventoryProviderSource now picks the included file that declares a provider's type over a root-config block for the same provider. Before, the root file always won because it is loaded last, so one stray inventory.providers.<p> block captured every later inv set, compat-fix, and group save.
  • PruneRootInventoryProviderShadow deletes the root's redundant copy of a host, group, or whole provider after its merged value is written to the owning file. This keeps the effective config the same while stopping the root from overriding the file. It does nothing when the root itself owns the provider.

Lookup names

  • Local hosts are added to the host catalog in Go map order, and a later name silently overwrote an earlier one. Lookup names are now ranked: full host name, then explicit alias, then derived short name. Ties go to the lexically first host.
  • inv set now skips an automatic short alias that another host already has (with a warning) and rejects an explicit --alias collision.

Overrides and local shadowing

  • New shared rules in internal/connect/provider_hosts.go, used by the catalog, the inv listing, and inv status/get:
    • A host under inventory.providers.<p>.hosts overlays the provider object it names; it is not a second host.
    • A local inventory host shadows a provider object with the same name.
  • Shadowing is decided when inventory is read, not by filtering during refresh. Local inventory is operator-owned and provider state is a refreshable cache, so this takes effect immediately and stays correct when local hosts are added or removed. Filtering during refresh would leave stale duplicates or missing hosts until the next refresh.
  • Previously the catalog let the cached NetBox object replace the local host, which dropped the local settings. The inv listing showed each override as its own row next to the cached NetBox object without the override applied, and findInventoryHost chose between those rows at random. inv commands now see one row per host with overrides applied, and local definitions win.
  • inv status <provider> lists shadowed objects and overrides that match no provider object; the dashboard shows the counts. inv get shows which NetBox object a local host shadows and suggests a provider override instead.

Docs

  • Updated skills/nssh/references/configuration-inventory-credentials.md: write ownership, lookup-name precedence, overlays, and shadowing.

Verification

  • make container-test: all packages pass, golangci-lint reports 0 issues, and the release cross-builds succeed.
  • New tests:
    • The root shadow is ignored on write and pruned afterward.
    • A provider owned by the root config is left alone.
    • A local upsert writes to the owning include.
    • A claimed short alias is skipped, and a claimed explicit alias is rejected.
    • Host lookup prefers the local definition.
    • Shared lookup names resolve deterministically.
    • A local host shadows a provider object in the catalog, and the findings list it.
    • Each provider host is listed once with its override applied, and status shows both kinds of finding.
  • Ran the branch build read-only against a real config with a 1,144-object NetBox cache. inv list went from 1,363 to 1,347 rows: 12 duplicate override rows and 4 shadowed objects are gone. Override rows now show the resolved username, and inv status netbox-prod names the 4 shadowed hosts.
  • TestManagedProxyAskpassPrefixRunsUnderFish fails on a macOS host with a fish shell on main too; it passes in the container.

Provider writes chose the last-merged source for inventory.providers.<p>,
so any root-config block for a provider captured every later write and
shadowed the included provider file. Prefer the included file that declares
the provider type, and prune the root's now-redundant host, group, or
provider copy after writing the merged value there.

findInventoryHost returned whichever same-named entry the unstable sort put
first, so local hosts listed by a provider too could be treated as
provider-owned and get root auth overrides. Prefer the local definition.

The host catalog filled aliases from Go map iteration with last-writer-wins,
so a short name shared by two hosts resolved randomly between them. Rank
names (full name > explicit alias > derived short name) and break ties by
canonical name. inv set now skips a derived short alias already owned by
another host and rejects a colliding explicit --alias.
The connect catalog and the inv commands resolved provider hosts with
different rules. The catalog treated provider YAML hosts as overlays but
let a cached provider object replace a local host of the same name; the
inv listing showed each override as a second row beside an unmerged
provider object, so inv set picked between them at random.

Share one rule set in internal/connect: a provider YAML host overlays the
provider object it names, and a local inventory host shadows a provider
object with the same name. Local inventory is operator-owned and provider
state is a refreshable cache, so precedence is decided at read time rather
than by filtering during refresh, and it takes effect without a refresh.

inv list/get/set now see one row per host with overrides applied. inv
status reports shadowed objects and overrides that match no provider
object; inv get shows what a local host shadows.
@ntwrknrd ntwrknrd changed the title fix(inventory): write to owning file and rank lookup names fix(inventory): own writes, overlays, and lookup names consistently Oct 28, 2026
@ntwrknrd
ntwrknrd merged commit 5e581ac into main Oct 28, 2026
3 checks passed
@ntwrknrd
ntwrknrd deleted the fix/inventory-write-target branch October 28, 2026 16:24
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