<#1145 Never orphan nodes on environment rename or...
# osctrl
g
#1145 Never orphan nodes on environment rename or delete Pull request opened by javuto Never orphan nodes on environment rename or delete Follow-up to #1144 (granular RBAC, part 1). Problem Nodes and posture rows point to their environment by name (
osquery_nodes.environment
,
node_posture.environment
), but neither rename nor delete updated them: • Rename changed only
tls_environments.name
. The environment's nodes stayed under the old name. • Delete removed the environment and left its nodes behind. Either way, the old name ended up free while nodes still carried it. Anyone who could create an environment with that name, including an environment manager since #1144, became its env admin and inherited the orphaned fleet: node lists, posture, and query and carve targeting. Renames also silently emptied the renamed environment. Change • Rename moves the data. New
EnvManager.UpdateFields(env, patch)
applies the patch and, on a name change, moves
osquery_nodes
and
node_posture
rows to the new name, all in one transaction.
PATCH /api/v1/environments/{env}
uses it. The old
shortcut:
comment is removed. • Delete is refused while nodes remain.
EnvManager.Delete
returns
ErrEnvHasNodes
.
DELETE /api/v1/environments/{env}
and the
delete
action on
POST /api/v1/environments
return 409. Soft-deleted nodes don't block. The CLI gets the same refusal through the shared package. • Names carried by orphaned nodes can't be reused. Create and rename return
ErrNameHasNodes
(409) if any live node still carries the target name. This also covers nodes orphaned before this fix in databases that are already deployed. Security / operational impact • Closes an escalation path: recreating a renamed or deleted environment's name to inherit its nodes. • Behavior change for operators: deleting an environment that still has enrolled nodes now fails with 409. Remove or move its nodes first. • Existing orphans are not migrated. They stay orphaned, but nobody can adopt them by creating an environment with that name. • Not covered: logging tables (
osquery_status_data
,
osquery_result_data
) keep the old name after a rename. They're only used for retention cleanup by name, so those rows won't be pruned by the renamed environment's per-environment retention. Not checked: whether TLS's Redis node cache holds the old environment name until it expires. Validation • New `pkg/environments/node_names_test.go`: • delete refused with live nodes and allowed with only soft-deleted ones; • create refused for an orphaned name; • rename moves nodes and posture rows and leaves other environments untouched; • a rename to an orphaned name is refused with nothing partially applied. • New
TestEnvNodesNeverOrphanedByRenameOrDelete
covers the full chain through the handlers: 409 on both delete routes; the rename moves the node; recreating the old name gives an empty environment; 409 for a name with orphaned nodes. • The new tests caught a real bug during development. GORM's
Updates
writes the new name back into the environment record, so without saving the old name first the migration would have moved zero nodes. • `go test ./...`: pass • `gofmt`: clean • `make openapi-check`: clean (409 responses were already documented) jmpsec/osctrl