Skip to content

feat(inventory): B2: serve an environment's inventory from canopy - #523

Open
dannash100 wants to merge 24 commits into
mainfrom
feat/canopy-inventory
Open

feat(inventory): B2: serve an environment's inventory from canopy#523
dannash100 wants to merge 24 commits into
mainfrom
feat/canopy-inventory

Conversation

@dannash100

@dannash100 dannash100 commented Aug 30, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-09-01 at 9 27 30 AM

Ansible reads each environment's servers and variables from a file nothing keeps in step with canopy, with salts and enrolment tokens in the clear.

  • POST /api/inventory/for_group: a group's live servers at one rank, with kind, address and vars.
  • Vars are the server's tags over its group's. true/false and JSON arrays decode.
  • Secret vars go to the secret store, per environment or per server. A name is a tag or a secret, never both.
  • Admin-only, and refused outright when a value can't be read.
  • Secret vars are marked so the ansible cache can drop them (beyondessential/ops#290).

Spec: .workhorse/specs/private-server/inventory.md. Lands after #524.

QA

Inventory from canopy

@dannash100 dannash100 changed the title feat(inventory): B2: serve a deployment's inventory from canopy feat(inventory): B2: serve an environment's inventory from canopy Aug 30, 2026

## Secrets

An inventory carries no secret.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, that's a problem, because there's secrets in the variables. so can't use that design

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh true that my bad

@dannash100
dannash100 marked this pull request as draft August 31, 2026 20:50
@dannash100
dannash100 marked this pull request as ready for review August 31, 2026 22:06
@dannash100
dannash100 requested a review from passcod September 2, 2026 03:03
///
/// Requires admin access, the inventory carrying the secret variables' values.
#[utoipa::path(
post,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a good candidate for the QUERY method, over POST?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently unsupported currently because
utoipa/utoipa-gen 5.5.0 and axum 0.8.9 are both the current max on crates.io.
And dont have that support 🙏

pub rank: Option<ServerRank>,
/// Identifier of the server, for a server-scoped variable.
#[serde(default)]
pub server_id: Option<Uuid>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably needs to become machine_id

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this entire type should probably be something like

enum ScopeArgs {
	Environment { server_group_id: Uuid, rank: ServerRank },
	Machine { machine_id: Uuid },
}

with the current structure, both of these invalid states are representable:

ScopeArgs {
	server_group_id: None,
	rank: None,
	server_id: None,
}

ScopeArgs {
	server_group_id: Some(...),
	rank: Some(...),
	server_id: Some(...)
}

and need careful handling; with the enum, they're not representable so bugs can't happen

let mut conn = state.db.get().await?;
reject_tag_of_that_name(&mut conn, scope, &args.name).await?;

let kube = secret_store(&state)?;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secret_store() is the shared BackupSecrets Kube backend, but the recovery-vault writer does not back up that backend generically: it snapshots only each ServerGroupBackupConfig.repo_password_ref. The new inventory-vars-* Secrets are therefore absent from state.age. A cluster/namespace loss loses these variables permanently, despite their being configuration-critical secrets. Extend the recovery snapshot (and restore design) to include declared inventory-secret keysets, or use a store whose contents are already covered by escrow.

…aintenance

feat(inventory): C2: refuse an inventory for work under way or an unplanned upgrade
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