@nitra/cfr 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # @nitra/cfr
2
2
 
3
3
  A handful of small k8s/GitOps CLI utilities, one `npx`/`bunx` away — no
4
- install, no dependencies. Two commands so far:
4
+ install. Three commands so far:
5
5
 
6
6
  - **`check`** (default) — verify a Kustomize directory's `resources:` list
7
7
  matches what's actually on disk
8
8
  - **`kcc-inventory`** — diff a GCP Config Connector namespace against the
9
9
  live project to find drift
10
+ - **`get-resources`** — the raw resource list `kcc-inventory` diffs,
11
+ without the diff
10
12
 
11
13
  ## `check`
12
14
 
@@ -146,6 +148,40 @@ already deleted in GCP. Both are cross-checked against a direct Compute
146
148
  Engine call before being reported, and any stale entry found this way is
147
149
  counted and noted separately — never silently folded into DRIFT.
148
150
 
151
+ ## `get-resources`
152
+
153
+ `kcc-inventory` is a diff on top of a fact-finding step: for each KCC
154
+ namespace, list what's live in GCP and what's declared under KCC. That
155
+ step is `get-resources` — same scan, same filtering, no drift/orphan
156
+ comparison. Useful on its own for piping into `jq`, feeding a different
157
+ tool, or just seeing everything a namespace touches without wading
158
+ through a diff.
159
+
160
+ ### Usage
161
+
162
+ ```sh
163
+ npx @nitra/cfr get-resources <namespace>
164
+ npx @nitra/cfr get-resources --all
165
+ npx @nitra/cfr get-resources <namespace|--all> --json
166
+ npx @nitra/cfr get-resources <namespace|--all> --include-system
167
+ ```
168
+
169
+ ```
170
+ ### namespace nitraai -> проєкт nitraai ###
171
+ == StorageBucket ==
172
+ gcp: 7n-forgejo-lfs
173
+ gcp: old-backups-bucket
174
+ kcc: 7n-forgejo-lfs
175
+ ```
176
+
177
+ `--json` emits `{resources: [{namespace, project, kind, id, source}, ...],
178
+ diagnostics: [...]}` — `source` is `"gcp"` or `"kcc"`, `diagnostics`
179
+ carries the same stale-cache/GKE-managed notes described above.
180
+
181
+ Same requirements as `kcc-inventory`: `kubectl` on `PATH`, Application
182
+ Default Credentials for the GCP side, `--include-system` to see
183
+ GCP-managed noise.
184
+
149
185
  ## License
150
186
 
151
187
  MIT
package/bin/cli.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { run as runCheck } from '../lib/check.mjs';
3
3
  import { run as runKccInventory } from '../lib/kcc-inventory.mjs';
4
+ import { run as runGetResources } from '../lib/get-resources.mjs';
4
5
 
5
6
  const TOP_HELP = `cfr (@nitra/cfr) — a handful of small k8s/GitOps CLI utilities
6
7
 
@@ -12,6 +13,7 @@ Commands:
12
13
  check Verify kustomization.yaml resources: match the directory
13
14
  (default when the first argument isn't a known command)
14
15
  kcc-inventory GCP Config Connector (KCC) drift inventory
16
+ get-resources Raw KCC/GCP resource list behind kcc-inventory, no diff
15
17
 
16
18
  Run "npx @nitra/cfr <command> --help" for command-specific help.
17
19
  `;
@@ -19,6 +21,7 @@ Run "npx @nitra/cfr <command> --help" for command-specific help.
19
21
  const COMMANDS = {
20
22
  check: runCheck,
21
23
  'kcc-inventory': runKccInventory,
24
+ 'get-resources': runGetResources,
22
25
  };
23
26
 
24
27
  // check is synchronous (local filesystem only); kcc-inventory is async
@@ -404,3 +404,110 @@ export async function collectNamespace(namespace, { includeSystem = false } = {}
404
404
 
405
405
  return { namespace, project, resources, diagnostics };
406
406
  }
407
+
408
+ // --- CLI: `cfr get-resources` — collectNamespace() as its own subcommand,
409
+ // no diffing. What kcc-inventory consumes, exposed directly for anyone
410
+ // who wants the raw facts (piping into jq, feeding a different tool).
411
+
412
+ export const HELP = `cfr get-resources — raw KCC/GCP resource list, no diff
413
+
414
+ Usage:
415
+ npx @nitra/cfr get-resources <namespace>
416
+ npx @nitra/cfr get-resources --all
417
+ npx @nitra/cfr get-resources <namespace|--all> [--json] [--include-system]
418
+
419
+ The mechanism kcc-inventory is built on, callable on its own: per KCC
420
+ namespace (namespace carrying the annotation
421
+ cnrm.cloud.google.com/project-id), lists every resource found live in the
422
+ GCP project (source: gcp) and every one declared as a Config Connector
423
+ custom resource in that namespace (source: kcc), for IAMServiceAccount,
424
+ IAMServiceAccountKey, ArtifactRegistryRepository, ContainerCluster,
425
+ ContainerNodePool, StorageBucket, ComputeAddress, DNSManagedZone,
426
+ DNSRecordSet, and IAMPolicyMember. No drift/orphan diff — that's
427
+ \`cfr kcc-inventory\`.
428
+
429
+ By default GCP-managed system noise is filtered out (Google-owned service
430
+ accounts, gcr.io shims, GKE-managed node pools/DNS zones, legacy bucket
431
+ ACL entries, ...) — pass --include-system to see it anyway.
432
+
433
+ Requires \`kubectl\` on PATH, pointed at the target cluster (set KUBE_CONTEXT
434
+ to target a specific kubeconfig context explicitly instead of relying on
435
+ the current one). The GCP side talks to Cloud Asset Inventory, IAM, and
436
+ Compute Engine directly over REST via Application Default Credentials —
437
+ no \`gcloud\` CLI needed.
438
+
439
+ Options:
440
+ --json Emit {resources, diagnostics} as JSON instead of the
441
+ human-readable listing.
442
+ --include-system Don't filter out GCP-managed system resources.
443
+ -h, --help Show this help and exit.
444
+
445
+ Exits 0 on a completed scan, 2 on a usage error.
446
+ `;
447
+
448
+ function printHuman(namespace, collected) {
449
+ console.log(`### namespace ${namespace} -> проєкт ${collected.project} ###`);
450
+ const byKind = new Map(KIND_ORDER.map((k) => [k, []]));
451
+ for (const r of collected.resources) byKind.get(r.kind)?.push(r);
452
+ for (const kind of KIND_ORDER) {
453
+ const entries = byKind.get(kind);
454
+ if (!entries.length) continue;
455
+ console.log(`== ${kind} ==`);
456
+ for (const { source, id } of [...entries].sort((a, b) => a.id.localeCompare(b.id))) {
457
+ console.log(` ${source}: ${id}`);
458
+ }
459
+ }
460
+ if (collected.diagnostics.length) {
461
+ console.log('== diagnostics ==');
462
+ for (const d of collected.diagnostics) {
463
+ if (d.type === 'stale-cache') console.log(` ${d.kind}: застарілий кеш Asset Inventory, ${d.count} запис(ів)`);
464
+ else if (d.type === 'gke-managed-skip') console.log(` ${d.kind}: керує сам GKE — ${d.zones.join(', ')}`);
465
+ }
466
+ }
467
+ console.log('');
468
+ }
469
+
470
+ export async function run(argv) {
471
+ if (argv.includes('-h') || argv.includes('--help')) {
472
+ process.stdout.write(HELP);
473
+ return 0;
474
+ }
475
+
476
+ const target = argv[0];
477
+ if (!target) {
478
+ console.error('usage: cfr get-resources <namespace|--all> [--json] [--include-system] (KUBE_CONTEXT=... для явного контексту)');
479
+ return 2;
480
+ }
481
+ const jsonMode = argv.includes('--json');
482
+ const includeSystem = argv.includes('--include-system');
483
+
484
+ let namespaces;
485
+ if (target === '--all') {
486
+ namespaces = listKccNamespaces();
487
+ if (!namespaces.length) {
488
+ console.error('жоден namespace не має cnrm.cloud.google.com/project-id');
489
+ return 2;
490
+ }
491
+ } else {
492
+ namespaces = [target];
493
+ }
494
+
495
+ const resources = [];
496
+ const diagnostics = [];
497
+ for (const namespace of namespaces) {
498
+ const collected = await collectNamespace(namespace, { includeSystem });
499
+ if (!collected.project) {
500
+ console.error(`namespace ${namespace} не має cnrm.cloud.google.com/project-id`);
501
+ continue;
502
+ }
503
+ if (jsonMode) {
504
+ for (const r of collected.resources) resources.push({ namespace, project: collected.project, ...r });
505
+ for (const d of collected.diagnostics) diagnostics.push({ namespace, project: collected.project, ...d });
506
+ } else {
507
+ printHuman(namespace, collected);
508
+ }
509
+ }
510
+
511
+ if (jsonMode) console.log(JSON.stringify({ resources, diagnostics }, null, 2));
512
+ return 0;
513
+ }
@@ -1,8 +1,8 @@
1
- // Diff і звіт над сирим списком з kcc-resources.mjs: що з GCP-ресурсів
1
+ // Diff і звіт над сирим списком з get-resources.mjs: що з GCP-ресурсів
2
2
  // проєкту реально живе під KCC (описано в git), а що — "чуже" (створено
3
3
  // повз KCC). Суто читання, нічого не видаляє — вхідний контракт для
4
4
  // окремого cleanup-рішення, яке вирішує, що з DRIFT можна прибрати.
5
- import { collectNamespace, listKccNamespaces, KIND_ORDER } from './kcc-resources.mjs';
5
+ import { collectNamespace, listKccNamespaces, KIND_ORDER } from './get-resources.mjs';
6
6
 
7
7
  export const HELP = `cfr kcc-inventory — GCP Config Connector (KCC) drift inventory
8
8
 
@@ -93,7 +93,7 @@ function printDiagnostic(diag, jsonMode, results, project) {
93
93
  }
94
94
  }
95
95
 
96
- // Groups the flat resources list from kcc-resources.mjs back into
96
+ // Groups the flat resources list from get-resources.mjs back into
97
97
  // per-kind {live, kcc} arrays, diffs and prints each kind in KIND_ORDER,
98
98
  // interleaving each kind's diagnostics (if any) right before its report —
99
99
  // same order a human reading top to bottom expects.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nitra/cfr",
3
- "version": "0.3.0",
4
- "description": "A handful of small k8s/GitOps CLI utilities: `check` verifies every YAML file in a Kustomize directory is listed in kustomization.yaml's explicit resources: (and vice versa); `kcc-inventory` diffs a GCP Config Connector namespace against the live project to find drift.",
3
+ "version": "0.4.0",
4
+ "description": "A handful of small k8s/GitOps CLI utilities: `check` verifies every YAML file in a Kustomize directory is listed in kustomization.yaml's explicit resources: (and vice versa); `kcc-inventory` diffs a GCP Config Connector namespace against the live project to find drift; `get-resources` is that same scan without the diff.",
5
5
  "keywords": [
6
6
  "kustomize",
7
7
  "flux",