@microagi/alchemy-gcp 0.5.1 → 0.7.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/lib/Auth/Credentials.d.ts +10 -4
  3. package/lib/Auth/Credentials.d.ts.map +1 -1
  4. package/lib/Auth/Credentials.js +13 -4
  5. package/lib/Auth/Credentials.js.map +1 -1
  6. package/lib/Compute/Address.d.ts +97 -0
  7. package/lib/Compute/Address.d.ts.map +1 -0
  8. package/lib/Compute/Address.js +143 -0
  9. package/lib/Compute/Address.js.map +1 -0
  10. package/lib/Compute/Firewall.d.ts +108 -0
  11. package/lib/Compute/Firewall.d.ts.map +1 -0
  12. package/lib/Compute/Firewall.js +163 -0
  13. package/lib/Compute/Firewall.js.map +1 -0
  14. package/lib/Compute/ForwardingRule.d.ts +106 -0
  15. package/lib/Compute/ForwardingRule.d.ts.map +1 -0
  16. package/lib/Compute/ForwardingRule.js +152 -0
  17. package/lib/Compute/ForwardingRule.js.map +1 -0
  18. package/lib/Compute/index.d.ts +3 -0
  19. package/lib/Compute/index.d.ts.map +1 -1
  20. package/lib/Compute/index.js +3 -0
  21. package/lib/Compute/index.js.map +1 -1
  22. package/lib/Container/Cluster.d.ts +20 -0
  23. package/lib/Container/Cluster.d.ts.map +1 -1
  24. package/lib/Container/Cluster.js +5 -2
  25. package/lib/Container/Cluster.js.map +1 -1
  26. package/lib/Container/NodePool.d.ts +26 -1
  27. package/lib/Container/NodePool.d.ts.map +1 -1
  28. package/lib/Container/NodePool.js +45 -8
  29. package/lib/Container/NodePool.js.map +1 -1
  30. package/lib/Kubernetes/KubernetesManifest.d.ts +100 -0
  31. package/lib/Kubernetes/KubernetesManifest.d.ts.map +1 -0
  32. package/lib/Kubernetes/KubernetesManifest.js +131 -0
  33. package/lib/Kubernetes/KubernetesManifest.js.map +1 -0
  34. package/lib/Kubernetes/Secret.d.ts +96 -0
  35. package/lib/Kubernetes/Secret.d.ts.map +1 -0
  36. package/lib/Kubernetes/Secret.js +173 -0
  37. package/lib/Kubernetes/Secret.js.map +1 -0
  38. package/lib/Kubernetes/client.d.ts +62 -0
  39. package/lib/Kubernetes/client.d.ts.map +1 -0
  40. package/lib/Kubernetes/client.js +233 -0
  41. package/lib/Kubernetes/client.js.map +1 -0
  42. package/lib/Kubernetes/connection.d.ts +48 -0
  43. package/lib/Kubernetes/connection.d.ts.map +1 -0
  44. package/lib/Kubernetes/connection.js +93 -0
  45. package/lib/Kubernetes/connection.js.map +1 -0
  46. package/lib/Kubernetes/index.d.ts +3 -0
  47. package/lib/Kubernetes/index.d.ts.map +1 -0
  48. package/lib/Kubernetes/index.js +3 -0
  49. package/lib/Kubernetes/index.js.map +1 -0
  50. package/lib/ManagedLustre/Instance.d.ts.map +1 -1
  51. package/lib/ManagedLustre/Instance.js +25 -5
  52. package/lib/ManagedLustre/Instance.js.map +1 -1
  53. package/lib/Providers.d.ts +1 -1
  54. package/lib/Providers.d.ts.map +1 -1
  55. package/lib/Providers.js +11 -1
  56. package/lib/Providers.js.map +1 -1
  57. package/lib/Sqladmin/Instance.d.ts.map +1 -1
  58. package/lib/Sqladmin/Instance.js +28 -6
  59. package/lib/Sqladmin/Instance.js.map +1 -1
  60. package/lib/index.d.ts +1 -0
  61. package/lib/index.d.ts.map +1 -1
  62. package/lib/index.js +1 -0
  63. package/lib/index.js.map +1 -1
  64. package/package.json +3 -1
  65. package/src/Auth/Credentials.ts +15 -5
  66. package/src/Compute/Address.ts +288 -0
  67. package/src/Compute/Firewall.ts +330 -0
  68. package/src/Compute/ForwardingRule.ts +301 -0
  69. package/src/Compute/index.ts +3 -0
  70. package/src/Container/Cluster.ts +25 -2
  71. package/src/Container/NodePool.ts +87 -13
  72. package/src/Kubernetes/KubernetesManifest.ts +301 -0
  73. package/src/Kubernetes/Secret.ts +348 -0
  74. package/src/Kubernetes/client.ts +386 -0
  75. package/src/Kubernetes/connection.ts +180 -0
  76. package/src/Kubernetes/index.ts +2 -0
  77. package/src/ManagedLustre/Instance.ts +35 -6
  78. package/src/Providers.ts +24 -0
  79. package/src/Sqladmin/Instance.ts +36 -7
  80. package/src/index.ts +1 -0
@@ -0,0 +1,173 @@
1
+ import { Credentials } from "@distilled.cloud/gcp";
2
+ import { createCoreV1NamespacedSecret, deleteCoreV1NamespacedSecret, readCoreV1NamespacedSecret, replaceCoreV1NamespacedSecret, } from "@distilled.cloud/kubernetes/core";
3
+ import { Resource } from "alchemy";
4
+ import { Unowned } from "alchemy/AdoptPolicy";
5
+ import { isResolved, somePropsAreDifferent } from "alchemy/Diff";
6
+ import { createPhysicalName } from "alchemy/PhysicalName";
7
+ import * as Provider from "alchemy/Provider";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Redacted from "effect/Redacted";
10
+ import { gcpInternalLabels, hasAlchemyLabels } from "../Tags.js";
11
+ import { clusterLayer } from "./connection.js";
12
+ export const KubernetesSecret = Resource("GCP.KubernetesSecret");
13
+ const toAttributes = (s, parent) => ({
14
+ name: s?.metadata?.name ?? parent.name,
15
+ namespace: s?.metadata?.namespace ?? parent.namespace,
16
+ endpoint: parent.endpoint,
17
+ caCertificate: parent.caCertificate,
18
+ type: s?.type ?? parent.type,
19
+ uid: s?.metadata?.uid,
20
+ resourceVersion: s?.metadata?.resourceVersion,
21
+ });
22
+ /** Mint a fresh GKE connection from props + ADC credentials. */
23
+ const connect = Effect.fn("k8sSecret.connect")(function* (props) {
24
+ const { accessToken } = yield* yield* Credentials;
25
+ return {
26
+ endpoint: props.endpoint,
27
+ caCertificate: props.caCertificate,
28
+ token: accessToken,
29
+ };
30
+ });
31
+ /** GET a Secret, mapping a 404 (`NotFound`) to `undefined`. */
32
+ const observe = (connection, namespace, name) => readCoreV1NamespacedSecret({ namespace, name }).pipe(Effect.provide(clusterLayer(connection)), Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
33
+ /** Base64-encode the (possibly redacted) string values for the `data` field. */
34
+ const encodeData = (stringData) => {
35
+ const data = {};
36
+ for (const [k, v] of Object.entries(stringData)) {
37
+ const raw = Redacted.isRedacted(v) ? Redacted.value(v) : v;
38
+ data[k] = Buffer.from(raw, "utf8").toString("base64");
39
+ }
40
+ return data;
41
+ };
42
+ export const KubernetesSecretProvider = () => Provider.effect(KubernetesSecret, Effect.gen(function* () {
43
+ return {
44
+ // Attributes unchanged by an in-place update (so dependents can resolve
45
+ // them at plan time): identity (name/namespace), the server-assigned
46
+ // uid, and endpoint — which is a replace trigger, so it never changes
47
+ // on update. caCertificate is intentionally excluded: it can rotate on
48
+ // the same cluster.
49
+ stables: ["name", "namespace", "endpoint", "uid"],
50
+ diff: Effect.fn(function* ({ id, news, olds = {} }) {
51
+ if (!isResolved(news))
52
+ return undefined;
53
+ const o = olds;
54
+ // Compare the RESOLVED name and type (with their defaults applied),
55
+ // not the raw props — otherwise omitting `name` on one side and
56
+ // setting it to the generated physical name on the other (or the
57
+ // same for `type`/"Opaque") triggers a spurious replace.
58
+ const defaultName = (yield* createPhysicalName({ id, maxLength: 63 })).toLowerCase();
59
+ const sameName = (o.name ?? defaultName) === (news.name ?? defaultName);
60
+ const sameType = (o.type ?? "Opaque") === (news.type ?? "Opaque");
61
+ // Replace (create new + delete old) rather than in-place upsert on:
62
+ // - `endpoint`: points at a different cluster (would orphan the old
63
+ // Secret). caCertificate is excluded — it can rotate on the same
64
+ // cluster and should just re-apply.
65
+ // - `namespace`/`name`: the object's identity.
66
+ // - `type`: immutable on a Kubernetes Secret; a PUT with a new type
67
+ // is rejected by the API.
68
+ if (!sameName ||
69
+ !sameType ||
70
+ somePropsAreDifferent(o, news, ["namespace", "endpoint"])) {
71
+ return { action: "replace" };
72
+ }
73
+ return undefined;
74
+ }),
75
+ reconcile: Effect.fn(function* ({ id, news }) {
76
+ const desiredName = news.name ??
77
+ (yield* createPhysicalName({ id, maxLength: 63 })).toLowerCase();
78
+ const desiredType = news.type ?? "Opaque";
79
+ const labels = {
80
+ ...(news.labels ?? {}),
81
+ ...(yield* gcpInternalLabels(id)),
82
+ };
83
+ const connection = yield* connect(news);
84
+ const layer = clusterLayer(connection);
85
+ const data = encodeData(news.stringData);
86
+ // Full-object PUT for an existing Secret — carries the observed
87
+ // resourceVersion (the API requires it for updates and uses it for
88
+ // optimistic concurrency).
89
+ const replaceFrom = (observed) => replaceCoreV1NamespacedSecret({
90
+ namespace: news.namespace,
91
+ name: desiredName,
92
+ fieldManager: "alchemy",
93
+ // Read-modify-write: a PUT replaces the WHOLE object, so preserve
94
+ // the observed metadata (annotations, ownerReferences, finalizers,
95
+ // …) and overlay only what we manage — otherwise every reconcile
96
+ // strips fields other managers set (e.g. an ownerReference, which
97
+ // would break garbage collection). `observed.metadata` carries the
98
+ // full server object at runtime (the local type is a subset).
99
+ // Labels merge (ours win) so foreign labels survive too. (The SSA
100
+ // path — GCP.KubernetesManifest — does field-level pruning instead;
101
+ // the typed PUT path deliberately preserves rather than prunes.)
102
+ metadata: {
103
+ ...observed.metadata,
104
+ name: desiredName,
105
+ labels: { ...observed.metadata?.labels, ...labels },
106
+ resourceVersion: observed.metadata?.resourceVersion,
107
+ },
108
+ type: desiredType,
109
+ data,
110
+ }).pipe(Effect.provide(layer));
111
+ // Observe → ensure: create if absent, else read-modify-write replace.
112
+ // Adoption (output defined, olds undefined) traverses the same flow.
113
+ const writeOnce = observe(connection, news.namespace, desiredName).pipe(Effect.flatMap((observed) => observed
114
+ ? replaceFrom(observed)
115
+ : createCoreV1NamespacedSecret({
116
+ namespace: news.namespace,
117
+ fieldManager: "alchemy",
118
+ metadata: { name: desiredName, labels },
119
+ type: desiredType,
120
+ data,
121
+ }).pipe(Effect.provide(layer))));
122
+ // Retry the WHOLE observe→write flow on Conflict — a stale
123
+ // resourceVersion (replace) or a create/delete race re-observes fresh
124
+ // state and writes again, so a second conflict is handled too. Bounded
125
+ // so a persistently-contended object fails (and the engine
126
+ // re-reconciles) rather than spinning forever.
127
+ const upsert = (attempt) => writeOnce.pipe(Effect.catchTag("Conflict", (e) => attempt < 3 ? upsert(attempt + 1) : Effect.fail(e)));
128
+ const result = yield* upsert(0);
129
+ return toAttributes(result, {
130
+ name: desiredName,
131
+ namespace: news.namespace,
132
+ endpoint: news.endpoint,
133
+ caCertificate: news.caCertificate,
134
+ type: desiredType,
135
+ });
136
+ }),
137
+ delete: Effect.fn(function* ({ output }) {
138
+ const connection = yield* connect(output);
139
+ yield* deleteCoreV1NamespacedSecret({
140
+ namespace: output.namespace,
141
+ name: output.name,
142
+ }).pipe(Effect.provide(clusterLayer(connection)),
143
+ // 404 is success (idempotent teardown).
144
+ Effect.catchTag("NotFound", () => Effect.void));
145
+ }),
146
+ read: Effect.fn(function* ({ id, output, olds }) {
147
+ const endpoint = output?.endpoint ?? olds?.endpoint;
148
+ const caCertificate = output?.caCertificate ?? olds?.caCertificate;
149
+ const namespace = output?.namespace ?? olds?.namespace;
150
+ if (!endpoint || !caCertificate || !namespace)
151
+ return undefined;
152
+ const name = output?.name ??
153
+ olds?.name ??
154
+ (yield* createPhysicalName({ id, maxLength: 63 })).toLowerCase();
155
+ const type = output?.type ?? olds?.type ?? "Opaque";
156
+ const connection = yield* connect({ endpoint, caCertificate });
157
+ const observed = yield* observe(connection, namespace, name);
158
+ if (!observed)
159
+ return undefined;
160
+ const attrs = toAttributes(observed, {
161
+ name,
162
+ namespace,
163
+ endpoint,
164
+ caCertificate,
165
+ type,
166
+ });
167
+ return (yield* hasAlchemyLabels(id, observed.metadata?.labels))
168
+ ? attrs
169
+ : Unowned(attrs);
170
+ }),
171
+ };
172
+ }));
173
+ //# sourceMappingURL=Secret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Secret.js","sourceRoot":"","sources":["../../src/Kubernetes/Secret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import * as Effect from "effect/Effect";
2
+ /** Connection details for a single GKE control plane. */
3
+ export interface GkeConnection {
4
+ /** Master endpoint — IP or hostname, no scheme (e.g. `34.1.2.3`). */
5
+ endpoint: string;
6
+ /** Base64-encoded cluster CA certificate (PEM), as GKE returns it. */
7
+ caCertificate: string;
8
+ /** Google OAuth access token (cloud-platform scope) for `Authorization`. */
9
+ token: string;
10
+ }
11
+ declare const KubernetesApiError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
12
+ readonly _tag: "KubernetesApiError";
13
+ } & Readonly<A>;
14
+ /** A non-2xx (or transport) response from the Kubernetes API server. */
15
+ export declare class KubernetesApiError extends KubernetesApiError_base<{
16
+ method: string;
17
+ path: string;
18
+ statusCode: number;
19
+ body: string;
20
+ }> {
21
+ }
22
+ /** A Kubernetes object as seen on the wire (only the fields we read back). */
23
+ export interface KubeObject {
24
+ apiVersion?: string;
25
+ kind?: string;
26
+ metadata?: {
27
+ name?: string;
28
+ namespace?: string;
29
+ uid?: string;
30
+ resourceVersion?: string;
31
+ labels?: Record<string, string>;
32
+ [key: string]: unknown;
33
+ };
34
+ [key: string]: unknown;
35
+ }
36
+ /** Where a Kind lives in the REST hierarchy, from the discovery doc. */
37
+ interface ResourceInfo {
38
+ /** Plural resource name, e.g. `secrets`, `deployments`. */
39
+ plural: string;
40
+ /** Whether instances are namespaced (vs cluster-scoped). */
41
+ namespaced: boolean;
42
+ }
43
+ /**
44
+ * Resolve a Kind to its REST resource (plural + scope) via the apiserver's
45
+ * discovery endpoint. This is what lets SSA work for ANY kind — built-ins and
46
+ * CRDs — without a hard-coded kind→plural table.
47
+ */
48
+ export declare const resolveResource: (connection: GkeConnection, apiVersion: string, kind: string) => Effect.Effect<ResourceInfo, KubernetesApiError>;
49
+ /**
50
+ * Server-side apply an arbitrary object (idempotent create-or-converge).
51
+ * `force=true` makes alchemy the field manager even where another manager
52
+ * currently owns a field we declare. Only the fields present in `object` are
53
+ * managed; fields owned by other managers are left untouched, and dropping a
54
+ * previously-declared field prunes it.
55
+ */
56
+ export declare const applyObject: (connection: GkeConnection, object: KubeObject, fieldManager?: string) => Effect.Effect<KubeObject | undefined, KubernetesApiError>;
57
+ /** GET an object; the caller maps `KubernetesApiError(404)` to "missing". */
58
+ export declare const getObject: (connection: GkeConnection, apiVersion: string, kind: string, namespace: string | undefined, name: string) => Effect.Effect<KubeObject, KubernetesApiError>;
59
+ /** DELETE an object; 404 is tolerated as success (idempotent teardown). */
60
+ export declare const deleteObject: (connection: GkeConnection, apiVersion: string, kind: string, namespace: string | undefined, name: string) => Effect.Effect<void, KubernetesApiError>;
61
+ export {};
62
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAgExC,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,aAAa,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;CACf;;;;AAED,wEAAwE;AACxE,qBAAa,kBAAmB,SAAQ,wBAEtC;IACA,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;CAAG;AA0GL,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wEAAwE;AACxE,UAAU,YAAY;IACpB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,EAAE,OAAO,CAAC;CACrB;AASD;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC1B,YAAY,aAAa,EACzB,YAAY,MAAM,EAClB,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,kBAAkB,CA0BhD,CAAC;AAuCF;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,YAAY,aAAa,EACzB,QAAQ,UAAU,EAClB,qBAAwB,KACvB,MAAM,CAAC,MAAM,CAAC,UAAU,GAAG,SAAS,EAAE,kBAAkB,CA+B1D,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,SAAS,GACpB,YAAY,aAAa,EACzB,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,WAAW,MAAM,GAAG,SAAS,EAC7B,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,kBAAkB,CAc5C,CAAC;AAEJ,2EAA2E;AAC3E,eAAO,MAAM,YAAY,GACvB,YAAY,aAAa,EACzB,YAAY,MAAM,EAClB,MAAM,MAAM,EACZ,WAAW,MAAM,GAAG,SAAS,EAC7B,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAkBtC,CAAC"}
@@ -0,0 +1,233 @@
1
+ import * as Data from "effect/Data";
2
+ import * as Effect from "effect/Effect";
3
+ import * as https from "node:https";
4
+ /**
5
+ * Raw, **server-side-apply-capable** GKE Kubernetes REST client.
6
+ *
7
+ * # Two ways this provider writes to a cluster — and when to use each
8
+ *
9
+ * There are two transports for talking to a GKE control plane. They are not
10
+ * redundant; each is correct for a different shape of resource.
11
+ *
12
+ * ### 1. Typed per-kind ops — `@distilled.cloud/kubernetes` via `connection.ts`
13
+ *
14
+ * What {@link "./Secret.ts"} uses. A **create + replace (PUT)** upsert built
15
+ * from the typed `core/v1` operations, with the cluster wired in by
16
+ * `clusterLayer`.
17
+ *
18
+ * **Use when** the kind is known at author time and the resource fully owns the
19
+ * object (Secret, ConfigMap, …).
20
+ * - ✅ Typed inputs/outputs (real `metadata`/`data`/`type` fields), no
21
+ * discovery round-trip, no content-type fiddling.
22
+ * - ⚠️ A PUT is a *full-object replacement*: it carries `resourceVersion` and
23
+ * overwrites every field — so it clobbers anything another manager set that
24
+ * you didn't include. Fine for objects you solely own; wrong for shared ones.
25
+ * - ⚠️ Per-kind only — there's a distinct typed op per Kind.
26
+ *
27
+ * ### 2. This raw client — server-side apply of an **arbitrary** object
28
+ *
29
+ * What {@link "./KubernetesManifest.ts"} uses. A single `PATCH` with
30
+ * `Content-Type: application/apply-patch+yaml`, `?fieldManager=alchemy&force=true`,
31
+ * the object itself as the (untyped) body, and the resource path resolved from
32
+ * the apiserver's **discovery** endpoint so *any* Kind works — built-ins and
33
+ * CRDs alike.
34
+ *
35
+ * **Use when** the kind is dynamic/unknown (generic manifests, CRDs), **or**
36
+ * you need server-side-apply semantics:
37
+ * - **field-level merge / ownership** — apply touches only the fields
38
+ * `fieldManager: alchemy` declares, leaving fields owned by other managers
39
+ * (controllers, defaulting, admission) intact instead of clobbering them;
40
+ * - **declarative pruning** — dropping a field you previously owned removes it,
41
+ * without a read-merge-write dance;
42
+ * - **one idempotent call** — no read-before-write, no create-vs-replace
43
+ * branch, no `resourceVersion` juggling.
44
+ * - ⚠️ Untyped body, plus one extra discovery `GET` to map Kind → resource.
45
+ *
46
+ * Rule of thumb: **known Kind you fully own → typed create/replace (path 1);
47
+ * arbitrary Kind or shared/merge semantics → SSA (this client).**
48
+ *
49
+ * ---
50
+ *
51
+ * Both transports authenticate with a Google OAuth bearer token (cloud-platform
52
+ * scope, minted from the provider's ADC credentials) and trust the cluster's
53
+ * own CA. `node:https` is used here deliberately (rather than the Effect
54
+ * `HttpClient`): the GKE API server presents a certificate signed by the
55
+ * *per-cluster* CA — not a public root — so the request must trust an explicit,
56
+ * runtime-resolved `ca` PEM. (`connection.ts`'s `clusterLayer` solves the same
57
+ * CA-trust problem for the distilled path, by overriding `FetchHttpClient`.)
58
+ * Calls are wrapped in `Effect.tryPromise` so failures surface as typed errors;
59
+ * a socket timeout bounds hung requests (Effect interruption won't abort an
60
+ * in-flight socket — the timeout is what guarantees a deploy can't block
61
+ * forever).
62
+ */
63
+ const REQUEST_TIMEOUT_MS = 30_000;
64
+ /** A non-2xx (or transport) response from the Kubernetes API server. */
65
+ export class KubernetesApiError extends Data.TaggedError("KubernetesApiError") {
66
+ }
67
+ const requestJson = Effect.fn("k8s.requestJson")(function* ({ connection, method, path, body, contentType, }) {
68
+ const url = new URL(path, `https://${connection.endpoint}`);
69
+ const payload = body ? JSON.stringify(body) : undefined;
70
+ const ca = Buffer.from(connection.caCertificate, "base64").toString("utf8");
71
+ return yield* Effect.tryPromise({
72
+ try: () => new Promise((resolve, reject) => {
73
+ const request = https.request({
74
+ protocol: url.protocol,
75
+ hostname: url.hostname,
76
+ port: url.port || 443,
77
+ path: `${url.pathname}${url.search}`,
78
+ method,
79
+ headers: {
80
+ Authorization: `Bearer ${connection.token}`,
81
+ Accept: "application/json",
82
+ ...(payload
83
+ ? {
84
+ "Content-Type": contentType ?? "application/json",
85
+ "Content-Length": Buffer.byteLength(payload),
86
+ }
87
+ : {}),
88
+ },
89
+ ca,
90
+ timeout: REQUEST_TIMEOUT_MS,
91
+ }, (response) => {
92
+ const chunks = [];
93
+ response.on("data", (chunk) => {
94
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
95
+ });
96
+ response.on("end", () => {
97
+ const responseBody = Buffer.concat(chunks).toString("utf8");
98
+ const statusCode = response.statusCode ?? 500;
99
+ if (statusCode < 200 || statusCode >= 300) {
100
+ reject(new KubernetesApiError({
101
+ method,
102
+ path,
103
+ statusCode,
104
+ body: responseBody,
105
+ }));
106
+ return;
107
+ }
108
+ if (!responseBody.trim()) {
109
+ resolve(undefined);
110
+ return;
111
+ }
112
+ try {
113
+ resolve(JSON.parse(responseBody));
114
+ }
115
+ catch {
116
+ resolve(responseBody);
117
+ }
118
+ });
119
+ });
120
+ request.on("error", reject);
121
+ // `timeout` only fires the event — we must destroy the socket
122
+ // ourselves so the promise rejects instead of hanging.
123
+ request.on("timeout", () => request.destroy(new KubernetesApiError({
124
+ method,
125
+ path,
126
+ statusCode: 0,
127
+ body: `request timed out after ${REQUEST_TIMEOUT_MS}ms`,
128
+ })));
129
+ if (payload)
130
+ request.write(payload);
131
+ request.end();
132
+ }),
133
+ catch: (error) => error instanceof KubernetesApiError
134
+ ? error
135
+ : new KubernetesApiError({
136
+ method,
137
+ path,
138
+ statusCode: 0,
139
+ body: error instanceof Error ? error.message : String(error),
140
+ }),
141
+ });
142
+ });
143
+ /**
144
+ * Discovery path for an `apiVersion`: core group (`v1`) lives under `/api/v1`,
145
+ * named groups (`apps/v1`, `networking.k8s.io/v1`) under `/apis/{group}/{ver}`.
146
+ */
147
+ const apiPathPrefix = (apiVersion) => apiVersion.includes("/") ? `/apis/${apiVersion}` : `/api/${apiVersion}`;
148
+ /**
149
+ * Resolve a Kind to its REST resource (plural + scope) via the apiserver's
150
+ * discovery endpoint. This is what lets SSA work for ANY kind — built-ins and
151
+ * CRDs — without a hard-coded kind→plural table.
152
+ */
153
+ export const resolveResource = (connection, apiVersion, kind) => {
154
+ const path = apiPathPrefix(apiVersion);
155
+ return requestJson({ connection, method: "GET", path }).pipe(Effect.flatMap((doc) => {
156
+ // Match on Kind, excluding subresources (their `name` contains a `/`,
157
+ // e.g. `pods/status`).
158
+ const match = (doc.resources ?? []).find((r) => r.kind === kind && !r.name.includes("/"));
159
+ return match
160
+ ? Effect.succeed({ plural: match.name, namespaced: match.namespaced })
161
+ : Effect.fail(new KubernetesApiError({
162
+ method: "GET",
163
+ path,
164
+ statusCode: 0,
165
+ body: `Kind ${apiVersion}/${kind} not found in discovery (${path})`,
166
+ }));
167
+ }));
168
+ };
169
+ const objectPath = (apiVersion, info, namespace, name) => {
170
+ const prefix = apiPathPrefix(apiVersion);
171
+ const collection = info.namespaced
172
+ ? `${prefix}/namespaces/${namespace}/${info.plural}`
173
+ : `${prefix}/${info.plural}`;
174
+ return name ? `${collection}/${name}` : collection;
175
+ };
176
+ /**
177
+ * Guard against building a `/namespaces/undefined/…` path: a namespaced Kind
178
+ * with no namespace would otherwise GET/DELETE a bogus URL — the API returns
179
+ * 404, which read treats as "absent" (phantom recreate) and delete tolerates as
180
+ * success (orphan). Fail loudly instead. (No-op for cluster-scoped Kinds.)
181
+ */
182
+ const requireNamespace = (method, apiVersion, kind, info, namespace) => info.namespaced && !namespace
183
+ ? Effect.fail(new KubernetesApiError({
184
+ method,
185
+ path: "",
186
+ statusCode: 0,
187
+ body: `${apiVersion}/${kind} is namespaced — a namespace is required`,
188
+ }))
189
+ : Effect.void;
190
+ /**
191
+ * Server-side apply an arbitrary object (idempotent create-or-converge).
192
+ * `force=true` makes alchemy the field manager even where another manager
193
+ * currently owns a field we declare. Only the fields present in `object` are
194
+ * managed; fields owned by other managers are left untouched, and dropping a
195
+ * previously-declared field prunes it.
196
+ */
197
+ export const applyObject = (connection, object, fieldManager = "alchemy") => {
198
+ const apiVersion = object.apiVersion;
199
+ const kind = object.kind;
200
+ const name = object.metadata?.name;
201
+ const namespace = object.metadata?.namespace;
202
+ if (!apiVersion || !kind || !name) {
203
+ return Effect.fail(new KubernetesApiError({
204
+ method: "PATCH",
205
+ path: "",
206
+ statusCode: 0,
207
+ body: "object must have apiVersion, kind and metadata.name",
208
+ }));
209
+ }
210
+ return resolveResource(connection, apiVersion, kind).pipe(Effect.flatMap((info) => requireNamespace("PATCH", apiVersion, kind, info, namespace).pipe(Effect.flatMap(() => {
211
+ const path = `${objectPath(apiVersion, info, namespace, name)}?fieldManager=${fieldManager}&force=true`;
212
+ return requestJson({
213
+ connection,
214
+ method: "PATCH",
215
+ path,
216
+ contentType: "application/apply-patch+yaml",
217
+ body: object,
218
+ });
219
+ }))));
220
+ };
221
+ /** GET an object; the caller maps `KubernetesApiError(404)` to "missing". */
222
+ export const getObject = (connection, apiVersion, kind, namespace, name) => resolveResource(connection, apiVersion, kind).pipe(Effect.flatMap((info) => requireNamespace("GET", apiVersion, kind, info, namespace).pipe(Effect.flatMap(() => requestJson({
223
+ connection,
224
+ method: "GET",
225
+ path: objectPath(apiVersion, info, namespace, name),
226
+ })))));
227
+ /** DELETE an object; 404 is tolerated as success (idempotent teardown). */
228
+ export const deleteObject = (connection, apiVersion, kind, namespace, name) => resolveResource(connection, apiVersion, kind).pipe(Effect.flatMap((info) => requireNamespace("DELETE", apiVersion, kind, info, namespace).pipe(Effect.flatMap(() => requestJson({
229
+ connection,
230
+ method: "DELETE",
231
+ path: objectPath(apiVersion, info, namespace, name),
232
+ })))), Effect.catchIf((e) => e instanceof KubernetesApiError && e.statusCode === 404, () => Effect.void));
233
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/Kubernetes/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { Credentials } from "@distilled.cloud/kubernetes/Credentials";
2
+ import * as Layer from "effect/Layer";
3
+ import type * as Redacted from "effect/Redacted";
4
+ import type * as HttpClient from "effect/unstable/http/HttpClient";
5
+ /**
6
+ * Bridges a GKE control plane into the layers that `@distilled.cloud/kubernetes`
7
+ * operations require — letting us drive the cluster's kube-apiserver through the
8
+ * typed distilled SDK (core/v1, apps/v1, batch, rbac, apiextensions/CRDs, …)
9
+ * instead of hand-rolling one HTTP resource per kind.
10
+ *
11
+ * distilled operations resolve two services from context:
12
+ *
13
+ * - `KubernetesCredentials` (`{ token, apiBaseUrl }`) — the bearer token (a
14
+ * Google OAuth access token minted from the provider's ADC `Credentials`) and
15
+ * the API base URL (`https://<cluster endpoint>`).
16
+ * - `HttpClient.HttpClient` — the transport. The GKE API server presents a cert
17
+ * signed by the cluster's *own* CA (not a public root), so a stock fetch fails
18
+ * TLS verification. We override {@link FetchHttpClient.Fetch} with a
19
+ * `node:https`-backed fetch that trusts the per-cluster CA — the same
20
+ * justified TLS exception the bespoke client made, but now routed through the
21
+ * typed SDK. (undici's `Agent`/`dispatcher` would be the fetch-native route,
22
+ * but undici isn't resolvable as a bare import here, and this reuses the
23
+ * transport we already trust.)
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * import { readCoreV1NamespacedSecret } from "@distilled.cloud/kubernetes/core";
28
+ *
29
+ * const secret = yield* readCoreV1NamespacedSecret({ namespace, name }).pipe(
30
+ * Effect.provide(clusterLayer({ endpoint, caCertificate, token })),
31
+ * );
32
+ * ```
33
+ */
34
+ /** A resolved connection to a single GKE control plane. */
35
+ export interface ClusterConnection {
36
+ /** Master endpoint — IP or hostname, no scheme (e.g. `34.1.2.3`). */
37
+ readonly endpoint: string;
38
+ /** Base64-encoded cluster CA certificate (PEM), as GKE returns it. */
39
+ readonly caCertificate: string;
40
+ /** Google OAuth access token (cloud-platform scope) for `Authorization`. */
41
+ readonly token: Redacted.Redacted<string>;
42
+ }
43
+ /**
44
+ * The full requirements layer for any `@distilled.cloud/kubernetes` operation
45
+ * against one GKE cluster: credentials + a CA-trusting HTTP client.
46
+ */
47
+ export declare const clusterLayer: (connection: ClusterConnection) => Layer.Layer<Credentials | HttpClient.HttpClient>;
48
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yCAAyC,CAAC;AAEtE,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAGnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC3C;AA2HD;;;GAGG;AACH,eAAO,MAAM,YAAY,GACvB,YAAY,iBAAiB,KAC5B,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,UAAU,CAAC,UAAU,CAI/C,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { Credentials } from "@distilled.cloud/kubernetes/Credentials";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
5
+ import * as https from "node:https";
6
+ /** Bounds a hung socket — Effect interruption can't abort an in-flight request. */
7
+ const REQUEST_TIMEOUT_MS = 30_000;
8
+ /**
9
+ * A `globalThis.fetch`-shaped function that issues the request over `node:https`
10
+ * trusting an explicit CA PEM, then resolves a standard `Response`. Effect's
11
+ * {@link FetchHttpClient.layer} reads this via `fiber.getRef(Fetch)` and feeds
12
+ * it `(url, { method, headers, body, signal })` per request.
13
+ */
14
+ const caFetch = (caPem) => ((input, init) => {
15
+ const href = typeof input === "string"
16
+ ? input
17
+ : input instanceof URL
18
+ ? input.href
19
+ : input.url;
20
+ const url = new URL(href);
21
+ const headers = {};
22
+ if (init?.headers) {
23
+ new Headers(init.headers).forEach((value, key) => {
24
+ headers[key] = value;
25
+ });
26
+ }
27
+ const body = init?.body;
28
+ if (body != null &&
29
+ typeof body !== "string" &&
30
+ !(body instanceof Uint8Array)) {
31
+ // Our k8s calls only ever send JSON (Raw/Uint8Array) bodies. A streaming
32
+ // body would need different handling — fail loudly rather than silently
33
+ // dropping it.
34
+ return Promise.reject(new TypeError("caFetch: only string/Uint8Array request bodies are supported"));
35
+ }
36
+ return new Promise((resolve, reject) => {
37
+ const request = https.request({
38
+ protocol: url.protocol,
39
+ hostname: url.hostname,
40
+ port: url.port || 443,
41
+ path: `${url.pathname}${url.search}`,
42
+ method: init?.method ?? "GET",
43
+ headers,
44
+ ca: caPem,
45
+ timeout: REQUEST_TIMEOUT_MS,
46
+ }, (response) => {
47
+ const chunks = [];
48
+ response.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
49
+ response.on("end", () => {
50
+ const buffer = Buffer.concat(chunks);
51
+ const responseHeaders = new Headers();
52
+ for (const [key, value] of Object.entries(response.headers)) {
53
+ if (Array.isArray(value)) {
54
+ for (const v of value)
55
+ responseHeaders.append(key, v);
56
+ }
57
+ else if (value != null) {
58
+ responseHeaders.set(key, value);
59
+ }
60
+ }
61
+ resolve(new Response(buffer.length > 0 ? buffer : null, {
62
+ status: response.statusCode ?? 500,
63
+ statusText: response.statusMessage ?? "",
64
+ headers: responseHeaders,
65
+ }));
66
+ });
67
+ });
68
+ request.on("error", reject);
69
+ // `timeout` only fires the event — destroy the socket ourselves so the
70
+ // promise rejects instead of hanging.
71
+ request.on("timeout", () => request.destroy(new Error(`Kubernetes request timed out after ${REQUEST_TIMEOUT_MS}ms`)));
72
+ // Honour Effect's abort signal so interruption tears down the socket.
73
+ if (init?.signal) {
74
+ if (init.signal.aborted)
75
+ request.destroy(new Error("aborted"));
76
+ else
77
+ init.signal.addEventListener("abort", () => request.destroy(new Error("aborted")));
78
+ }
79
+ if (body != null)
80
+ request.write(body);
81
+ request.end();
82
+ });
83
+ });
84
+ /** `HttpClient` layer whose fetch trusts the given (base64 PEM) cluster CA. */
85
+ const caHttpClient = (caCertificate) => Layer.provide(FetchHttpClient.layer, Layer.succeed(FetchHttpClient.Fetch, caFetch(Buffer.from(caCertificate, "base64").toString("utf8"))));
86
+ /** `KubernetesCredentials` layer for a single cluster + bearer token. */
87
+ const credentials = (endpoint, token) => Layer.succeed(Credentials, Effect.succeed({ token, apiBaseUrl: `https://${endpoint}` }));
88
+ /**
89
+ * The full requirements layer for any `@distilled.cloud/kubernetes` operation
90
+ * against one GKE cluster: credentials + a CA-trusting HTTP client.
91
+ */
92
+ export const clusterLayer = (connection) => Layer.merge(credentials(connection.endpoint, connection.token), caHttpClient(connection.caCertificate));
93
+ //# sourceMappingURL=connection.js.map