@jr2/cli 0.3.0 → 0.5.1

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/src/output.ts CHANGED
@@ -24,7 +24,7 @@ export type Io = {
24
24
  kubeAdmin?: KubeAdmin;
25
25
  /** Override the image build port; default → pnpm + docker + kind subprocesses. */
26
26
  build?: BuildPort;
27
- /** Override `jr2 up`'s Instance typecheck (ADR-0050); default → the Instance's own `tsc`. */
27
+ /** Override the Instance typecheck of `jr2 up` and `jr2 check` (ADR-0050); default → the Instance's own `tsc`. */
28
28
  typecheck?: TypecheckPort;
29
29
  /** Where kit-checkout detection starts walking up from (ADR-0038); default → the CLI's own
30
30
  * module directory, which is the whole signal: a checkout resolves the kit sources, an npm
package/src/repo-sweep.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  // no Repos — the one read that may answer "none" — while any other failure throws and the caller
12
12
  // sweeps nothing.
13
13
 
14
- import { ANNOTATION_REPO_LAST_ATTACHED, LABEL_REPO_BOUND } from "@jr2/orchestrator";
14
+ import { ANNOTATION_REPO_LAST_ATTACHED, LABEL_REPO_BOUND } from "@jr2/orchestrator/internal";
15
15
  import { LABEL_INSTANCE } from "./deploy.ts";
16
16
  import { isMissingResourceType, type KubeAdmin } from "./kube.ts";
17
17
  import { activity, type Io } from "./output.ts";
package/src/sweep.ts CHANGED
@@ -7,11 +7,12 @@
7
7
  // is not this instance's garbage):
8
8
  // 1. the `jr2-images` ConfigMap of every jr2 instance (namespaces labeled `jr2.dev/instance`) — what
9
9
  // FUTURE Sandboxes will be provisioned with;
10
- // 2. every Sandbox CR's `spec.image` in those namespaces — a parked Workspace must survive a pod
11
- // restart, and `imagePullPolicy: IfNotPresent` cannot re-pull a local tag;
12
- // 3. every pod's container images in those namespaces plus `jr2-system` — what is actually running
13
- // (orchestrator, Instance Harness, Custodians, Sandboxes, operator), mid-roll pods INCLUDED,
14
- // without naming Deployments one by one.
10
+ // 2. every Sandbox CR's `spec.image`, sidecar, init container and `image` volume in those
11
+ // namespaces — a parked Workspace must survive a pod restart, and `imagePullPolicy:
12
+ // IfNotPresent` cannot re-pull a local tag;
13
+ // 3. every pod's container and `image` volume refs in those namespaces plus `jr2-system` — what is
14
+ // actually running (orchestrator, Instance Harness, Custodians, Sandboxes, operator), mid-roll
15
+ // pods INCLUDED, without naming Deployments one by one.
15
16
  // The keep set is their union. "Kit images are never pruned" is not a rule here: a kit ref is kept
16
17
  // because some instance's map or pod names it, and collects like anything else when the last
17
18
  // instance leaves the cluster.
@@ -23,12 +24,13 @@
23
24
  // all: a cluster with no Sandbox CRD holds no Sandbox CRs, so that read's "none" is complete (see
24
25
  // `listSandboxes`).
25
26
  //
26
- // Accepted, and written down so nobody adds a name filter to "fix" it (ADR-0039): the host keep set
27
- // only sees the CURRENT context's roots, so a second checkout converging to a different cluster can
28
- // have its host kit generation swept. The rebuild is BuildKit-cached seconds. Re-introducing a name
29
- // filter would resurrect the exact primitive this ADR deletes.
27
+ // Written down so nobody adds a name filter to "fix" it (ADR-0039): the host keep set only sees the
28
+ // CURRENT context's roots, so a converge to a different cluster — a second checkout's, or this
29
+ // one's — can lose a host image it built and has not used yet. `jr2 up` answers that itself: a tag
30
+ // it counted on and finds missing is rebuilt (BuildKit-cached seconds) and the use retried.
31
+ // Re-introducing a name filter would resurrect the exact primitive this ADR deletes.
30
32
 
31
- import { IMAGES_CONFIGMAP, IMAGES_KEY } from "@jr2/orchestrator";
33
+ import { IMAGES_CONFIGMAP, IMAGES_KEY } from "@jr2/orchestrator/internal";
32
34
  import { formatBytes, mergeSweeps, sweepHost, sweepNodes, type BuildPort, type SweepResult } from "./build.ts";
33
35
  import { LABEL_INSTANCE, OPERATOR_NAMESPACE } from "./deploy.ts";
34
36
  import { isMissingResourceType, type KubeAdmin } from "./kube.ts";
@@ -53,13 +55,18 @@ export type Roots = { keep: Set<string>; namespaces: string[] };
53
55
 
54
56
  type NamespacedObject = { metadata: { name: string; namespace?: string } };
55
57
  type ConfigMapObject = NamespacedObject & { data?: Record<string, string> };
56
- type SandboxObject = NamespacedObject & { spec?: { image?: string; sidecars?: ContainerSpec[] } };
58
+ type SandboxObject = NamespacedObject & {
59
+ spec?: { image?: string; sidecars?: ContainerSpec[]; initContainers?: ContainerSpec[]; volumes?: VolumeSpec[] };
60
+ };
57
61
  type ContainerSpec = { image?: string };
62
+ /** Only an `image` volume names a ref: every Sandbox's `/opt/jr2` is one, of the harness image (ADR-0037). */
63
+ type VolumeSpec = { image?: { reference?: string } };
58
64
  type PodObject = NamespacedObject & {
59
65
  spec?: {
60
66
  containers?: ContainerSpec[];
61
67
  initContainers?: ContainerSpec[];
62
68
  ephemeralContainers?: ContainerSpec[];
69
+ volumes?: VolumeSpec[];
63
70
  };
64
71
  };
65
72
 
@@ -93,18 +100,20 @@ export async function readRoots(kube: KubeAdmin, ctx: { context?: string } = {})
93
100
  }
94
101
 
95
102
  // 2. what a parked Workspace's pod will be recreated with — the Sandbox Image AND every sidecar's
96
- // ref. The CR always carries the Custodian as a sidecar (sandbox-kubectl.ts), and its ref is not
103
+ // ref. The CR always carries the Custodian as a sidecar (sandbox-kube.ts), and its ref is not
97
104
  // covered by the other roots: a running Sandbox is deliberately never re-imaged, so an `up` that
98
105
  // moved the Custodian's pin leaves the CR naming the OLD one while the map names the new. If that pod
99
106
  // is then lost (node restart, eviction, drain), the recreated one pulls the CR's sidecar ref —
100
- // and `IfNotPresent` cannot re-pull a local tag a sweep took.
107
+ // and `IfNotPresent` cannot re-pull a local tag a sweep took. The same holds for the harness ref: it
108
+ // reaches the pod only as the `runtime` image volume (ADR-0037), never as a container's image.
101
109
  const sandboxes = await listSandboxes(kube, ctx);
102
110
  for (const sandbox of sandboxes) {
103
111
  if (!instanceNs.has(sandbox.metadata.namespace ?? "")) continue;
104
112
  if (sandbox.spec?.image) keep.add(sandbox.spec.image);
105
- for (const sidecar of sandbox.spec?.sidecars ?? []) {
106
- if (sidecar.image) keep.add(sidecar.image);
113
+ for (const c of [...(sandbox.spec?.sidecars ?? []), ...(sandbox.spec?.initContainers ?? [])]) {
114
+ if (c.image) keep.add(c.image);
107
115
  }
116
+ for (const ref of volumeRefs(sandbox.spec?.volumes)) keep.add(ref);
108
117
  }
109
118
 
110
119
  // 3. what is running right now — plus the operator, which lives in the shared `jr2-system`.
@@ -116,11 +125,17 @@ export async function readRoots(kube: KubeAdmin, ctx: { context?: string } = {})
116
125
  for (const c of [...(spec.containers ?? []), ...(spec.initContainers ?? []), ...(spec.ephemeralContainers ?? [])]) {
117
126
  if (c.image) keep.add(c.image);
118
127
  }
128
+ for (const ref of volumeRefs(spec.volumes)) keep.add(ref);
119
129
  }
120
130
 
121
131
  return { keep, namespaces };
122
132
  }
123
133
 
134
+ /** The refs a volume list mounts as `image` volumes — invisible to a read of containers alone. */
135
+ function volumeRefs(volumes: VolumeSpec[] | undefined): string[] {
136
+ return (volumes ?? []).flatMap((v) => (v.image?.reference ? [v.image.reference] : []));
137
+ }
138
+
124
139
  /**
125
140
  * Root #2's read, with the ONE degradation the fail-closed rule allows: a cluster that has no
126
141
  * `sandboxes.core.jr2.dev` resource type can hold no Sandbox CRs, so "no Sandboxes" is the complete