feat: add whole-group export/import for worker groups and edge fleets - #1
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new groups CLI command for exporting/importing an entire worker group or edge fleet configuration, backed by a new group_transfer utility module and unit tests.
Changes:
- Introduces
cribl_cli.utils.group_transferto collect per-group resources into an export payload and apply an import payload with safety flags (routes/packs/lookups/sensitive). - Adds a new
cribl groups export|importClick command and registers it in the main CLI. - Adds unit tests and updates
CLAUDE.mddocumentation for the new feature.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
cribl_cli/utils/group_transfer.py |
Implements whole-group collect/export, import/apply, and directory-based payload IO. |
cribl_cli/commands/groups.py |
Adds groups export and groups import CLI commands with deploy confirmation safeguards. |
cribl_cli/cli.py |
Registers the new groups command group in the root CLI. |
tests/unit/test_group_transfer.py |
Adds unit tests for resource planning, export aggregation, IO roundtrip, and import safety. |
CLAUDE.md |
Documents the new command and its safety rules/conventions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
write_dir() chmods the export directory to 0700 and each file to 0600, so --include-sensitive exports are not left world-readable (mirrors the ~/.criblrc 0600 protection). read_input() raises on an ambiguous directory (no _meta.json plus multiple group subdirectories) instead of silently returning an empty payload that would make import a no-op. Addresses Copilot review feedback on PR #1. Assisted-by: Claude:claude-opus-4-8[1m]
Eight group-scoped resources used endpoint paths that return HTTP 404 on Cribl 4.17.1 (verified live against a real Leader). The originals were inconsistent — some under system/, one with no library prefix, two with a nested lib/sds/... segment, and several with abbreviated names — so the commands built from them could never reach the API. parsers system/parsers -> lib/parsers schemas schemas -> lib/schemas db-connections lib/db-connections -> lib/database-connections conditions lib/conditions -> conditions sds-rules lib/sds/rules -> lib/sds-rules sds-rulesets lib/sds/rulesets -> lib/sds-rulesets appscope lib/appscope -> lib/appscope-configs hmac-functions lib/hmac -> lib/hmac-functions registry.py drives every factory-generated CRUD command, so this also repairs the existing `cribl parsers list`, `schemas`, `sds-rules`, `appscope`, etc. commands — not just the new group export/import that surfaced the problem.
Add a `groups` command that moves an entire worker group's or edge fleet's config between groups (a fleet is the same API object with `isFleet: true`, so both flow through one path): - `groups export <group>` pulls every group-scoped resource to stdout, or `--out-dir` writes one file per resource type (dir/files locked to 0700/0600). - `groups import <group>` upserts the config back. It is staged, never deployed: routes are skipped unless `--with-routes`, and `--deploy` requires `--yes`. - Secrets, credentials, and certificates are excluded unless `--include-sensitive`; everything skipped or failed is reported in `_meta`. - Import honors each resource type's registry operations: read-only types (executors, hmac-functions, functions) are skipped and create-only types without `update` (certificates, samples, scripts) are created, not PATCHed. The resource list is derived from `commands/registry.py` plus the hand-written sources/destinations/pipelines/packs/routes, so it never drifts from the rest of the CLI. Route import uses a new public `replace_route_table()` helper in `api/endpoints/routes.py` (wholesale swap, preserving edge/stream format). Depends on the registry path corrections in #2 (the export/import surfaced the 404s those fix).
31cfefc to
9d899e3
Compare
- write_dir() clears stale *.json from a prior export before writing, so a secrets.json left by an earlier --include-sensitive run can't linger and be re-imported by read_input(). - Narrow the "never replace the route table wholesale" rule in CLAUDE.md to note that `groups import --with-routes` is the deliberate, gated exception. - Add unit tests for replace_route_table() (stream + edge wrapper) and for write_dir() stale-file cleanup.
Whole-group export captured Cribl-shipped library content (lib == "cribl"), built-in system objects (destroyable false), and pack-owned items (id prefixed "pack:") because they appear in list responses. None are writable as standalone group config, so importing them produced hundreds of 4xx/5xx errors. Filter them at export so the payload is only user-authored config. Verified live: a real group's import failures dropped from ~300 to ~14 (the remainder being pack archives, which need the .crbl, and read-only system conditions). The dropped count is reported in _meta.skipped.builtin. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Pushed an additional commit (6d75f82) on top of this branch: export now filters out non-importable resources. While live-testing the import path (round-trip into a throwaway group), the export was capturing Cribl-shipped content that can't be written back — built-in library items ( The commit drops these at export so the payload is only user-authored config. Verified live: import failures fell from ~300 → 14. The remaining 14 are inherent and reported gracefully:
Dropped count is surfaced in |
werd-drew
left a comment
There was a problem hiding this comment.
Approved. Live-validated end to end against our Leader: export (read-only) pulls all user-authored group config; import merges by id (creates then updates), never deletes target-only resources, and only replaces the route table with --with-routes. Added commit 6d75f82 filters built-in/pack-owned content so real-world import is clean (failures ~300 → 14). 143 unit tests pass. 🚢
Summary
Adds a
groupscommand that moves an entire worker group's or edge fleet'sconfig between groups. A fleet is the same API object as a worker group
(
isFleet: true), so both flow through one path. The resource list is derivedfrom
commands/registry.pyplus the hand-written sources/destinations/pipelines/packs/routes, so it never drifts from the rest of the CLI.
Changes
commands/groups.py—groups export/groups importCLI:export <group>→ all group-scoped config to stdout, or--out-dirfor onefile per resource type (dir/files locked to
0700/0600).import <group>→ staged upsert; never auto-deploys. Routes skipped unless--with-routes;--deployrequires--yes.--include-sensitive;what was skipped or failed is reported in
_meta.utils/group_transfer.py— the export/import engine. Import honors eachresource type's registry
operations: read-only types (executors,hmac-functions, functions) are skipped, and create-only types without
update(certificates, samples, scripts) are created, not PATCHed.api/endpoints/routes.py— new publicreplace_route_table()(wholesaleswap preserving edge/stream format), so group import no longer imports
private route helpers.
cli.pyregisters the command;CLAUDE.mddocuments it.Test Plan
pytest -m "not integration"— 142 unit tests pass.cribl groups --help/groups export --help/groups import --helpwire up.pytest -m integration) needCRIBL_INTEGRATION_TEST=true🤖 Generated with Claude Code