@microagi/alchemy-gcp 0.6.0 → 0.8.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/CHANGELOG.md +86 -0
- package/lib/Iam/ServiceAccount.d.ts +104 -0
- package/lib/Iam/ServiceAccount.d.ts.map +1 -0
- package/lib/Iam/ServiceAccount.js +236 -0
- package/lib/Iam/ServiceAccount.js.map +1 -0
- package/lib/Iam/ServiceAccountIamMember.d.ts +49 -0
- package/lib/Iam/ServiceAccountIamMember.d.ts.map +1 -0
- package/lib/Iam/ServiceAccountIamMember.js +50 -0
- package/lib/Iam/ServiceAccountIamMember.js.map +1 -0
- package/lib/Iam/index.d.ts +4 -0
- package/lib/Iam/index.d.ts.map +1 -0
- package/lib/Iam/index.js +3 -0
- package/lib/Iam/index.js.map +1 -0
- package/lib/Kubernetes/KubernetesManifest.d.ts +100 -0
- package/lib/Kubernetes/KubernetesManifest.d.ts.map +1 -0
- package/lib/Kubernetes/KubernetesManifest.js +131 -0
- package/lib/Kubernetes/KubernetesManifest.js.map +1 -0
- package/lib/Kubernetes/Secret.d.ts +25 -18
- package/lib/Kubernetes/Secret.d.ts.map +1 -1
- package/lib/Kubernetes/Secret.js +79 -45
- package/lib/Kubernetes/Secret.js.map +1 -1
- package/lib/Kubernetes/client.d.ts +27 -23
- package/lib/Kubernetes/client.d.ts.map +1 -1
- package/lib/Kubernetes/client.js +139 -37
- package/lib/Kubernetes/client.js.map +1 -1
- package/lib/Kubernetes/connection.d.ts +48 -0
- package/lib/Kubernetes/connection.d.ts.map +1 -0
- package/lib/Kubernetes/connection.js +93 -0
- package/lib/Kubernetes/connection.js.map +1 -0
- package/lib/Kubernetes/index.d.ts +1 -0
- package/lib/Kubernetes/index.d.ts.map +1 -1
- package/lib/Kubernetes/index.js +1 -0
- package/lib/Kubernetes/index.js.map +1 -1
- package/lib/Providers.d.ts.map +1 -1
- package/lib/Providers.js +5 -1
- package/lib/Providers.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +4 -2
- package/src/Iam/ServiceAccount.ts +431 -0
- package/src/Iam/ServiceAccountIamMember.ts +56 -0
- package/src/Iam/index.ts +3 -0
- package/src/Kubernetes/KubernetesManifest.ts +301 -0
- package/src/Kubernetes/Secret.ts +153 -79
- package/src/Kubernetes/client.ts +226 -59
- package/src/Kubernetes/connection.ts +180 -0
- package/src/Kubernetes/index.ts +1 -0
- package/src/Providers.ts +12 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Credentials } from "@distilled.cloud/gcp";
|
|
2
|
+
import { Resource } from "alchemy";
|
|
3
|
+
import * as Provider from "alchemy/Provider";
|
|
4
|
+
import type * as GCP from "../Providers.ts";
|
|
5
|
+
/**
|
|
6
|
+
* Apply an **arbitrary** Kubernetes object to a GKE cluster via server-side
|
|
7
|
+
* apply — the generic escape hatch for any Kind (built-ins and CRDs alike),
|
|
8
|
+
* complementing the typed {@link "./Secret.ts"} resource.
|
|
9
|
+
*
|
|
10
|
+
* Convergence is a single `application/apply-patch+yaml` PATCH with
|
|
11
|
+
* `fieldManager: alchemy`, `force: true`. SSA merges by field ownership: only
|
|
12
|
+
* the fields you declare are managed, fields owned by other managers/controllers
|
|
13
|
+
* are left intact, and dropping a previously-declared field prunes it. The REST
|
|
14
|
+
* path is resolved from the apiserver's discovery endpoint, so no per-Kind code
|
|
15
|
+
* is required. See {@link "./client.ts"} for the full create/replace-vs-SSA
|
|
16
|
+
* rationale and when to prefer each.
|
|
17
|
+
*
|
|
18
|
+
* Ownership for adoption is gated on alchemy-internal `metadata.labels` (same
|
|
19
|
+
* triple as labelled GCP resources), so an object created out of band reads
|
|
20
|
+
* back as {@link Unowned} until adopted.
|
|
21
|
+
*
|
|
22
|
+
* @section Applying a manifest
|
|
23
|
+
* @example A ConfigMap
|
|
24
|
+
* ```typescript
|
|
25
|
+
* yield* GCP.KubernetesManifest("AppConfig", {
|
|
26
|
+
* endpoint: cluster.endpoint,
|
|
27
|
+
* caCertificate: cluster.clusterCaCertificate,
|
|
28
|
+
* apiVersion: "v1",
|
|
29
|
+
* kind: "ConfigMap",
|
|
30
|
+
* name: "app-config",
|
|
31
|
+
* namespace: "admin",
|
|
32
|
+
* body: { data: { LOG_LEVEL: "info" } },
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
* @example A namespaced CRD instance
|
|
36
|
+
* ```typescript
|
|
37
|
+
* yield* GCP.KubernetesManifest("Tunnel", {
|
|
38
|
+
* endpoint: cluster.endpoint,
|
|
39
|
+
* caCertificate: cluster.clusterCaCertificate,
|
|
40
|
+
* apiVersion: "networking.cfargotunnel.com/v1alpha1",
|
|
41
|
+
* kind: "TunnelBinding",
|
|
42
|
+
* name: "research-ui",
|
|
43
|
+
* namespace: "admin",
|
|
44
|
+
* body: { spec: { ... } },
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export type KubernetesManifestProps = {
|
|
49
|
+
/**
|
|
50
|
+
* GKE control-plane endpoint (IP or hostname, no scheme) — typically
|
|
51
|
+
* `cluster.endpoint`. A change points at a different cluster → replace.
|
|
52
|
+
*/
|
|
53
|
+
endpoint: string;
|
|
54
|
+
/**
|
|
55
|
+
* Base64-encoded cluster CA certificate (PEM) — typically
|
|
56
|
+
* `cluster.clusterCaCertificate`. Mutable (can rotate on the same cluster).
|
|
57
|
+
*/
|
|
58
|
+
caCertificate: string;
|
|
59
|
+
/** Object `apiVersion`, e.g. `"v1"`, `"apps/v1"`. Immutable — replace. */
|
|
60
|
+
apiVersion: string;
|
|
61
|
+
/** Object `kind`, e.g. `"ConfigMap"`, `"Deployment"`. Immutable — replace. */
|
|
62
|
+
kind: string;
|
|
63
|
+
/** `metadata.name`. Immutable — replace. */
|
|
64
|
+
name: string;
|
|
65
|
+
/**
|
|
66
|
+
* `metadata.namespace`. Required for namespaced Kinds, omit for cluster-scoped
|
|
67
|
+
* ones. Immutable — replace.
|
|
68
|
+
*/
|
|
69
|
+
namespace?: string;
|
|
70
|
+
/** Extra `metadata.labels` (alchemy ownership labels merge on top). Mutable. */
|
|
71
|
+
labels?: Record<string, string>;
|
|
72
|
+
/**
|
|
73
|
+
* The rest of the object — everything except `apiVersion`/`kind`/`metadata`
|
|
74
|
+
* (e.g. `spec`, `data`, `rules`). Mutable; may carry resolved Inputs from
|
|
75
|
+
* other resources. SSA prunes any key dropped from a later apply.
|
|
76
|
+
*/
|
|
77
|
+
body?: Record<string, unknown>;
|
|
78
|
+
};
|
|
79
|
+
export type KubernetesManifestAttributes = {
|
|
80
|
+
/** Object apiVersion. */
|
|
81
|
+
apiVersion: string;
|
|
82
|
+
/** Object kind. */
|
|
83
|
+
kind: string;
|
|
84
|
+
/** metadata.name. */
|
|
85
|
+
name: string;
|
|
86
|
+
/** metadata.namespace (undefined for cluster-scoped objects). */
|
|
87
|
+
namespace: string | undefined;
|
|
88
|
+
/** Server-assigned uid. */
|
|
89
|
+
uid: string | undefined;
|
|
90
|
+
/** Server-assigned resourceVersion at last apply. */
|
|
91
|
+
resourceVersion: string | undefined;
|
|
92
|
+
/** Control-plane endpoint, threaded through for delete/read. */
|
|
93
|
+
endpoint: string;
|
|
94
|
+
/** Cluster CA certificate, threaded through for delete/read. */
|
|
95
|
+
caCertificate: string;
|
|
96
|
+
};
|
|
97
|
+
export type KubernetesManifest = Resource<"GCP.KubernetesManifest", KubernetesManifestProps, KubernetesManifestAttributes, never, GCP.Providers>;
|
|
98
|
+
export declare const KubernetesManifest: import("alchemy").ResourceClass<KubernetesManifest>;
|
|
99
|
+
export declare const KubernetesManifestProvider: () => import("effect/Layer").Layer<Provider.Provider<KubernetesManifest>, never, import("alchemy").Stack | import("alchemy").Stage | Credentials>;
|
|
100
|
+
//# sourceMappingURL=KubernetesManifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KubernetesManifest.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/KubernetesManifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAW5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,2BAA2B;IAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,qDAAqD;IACrD,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CACvC,wBAAwB,EACxB,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,EACL,GAAG,CAAC,SAAS,CACd,CAAC;AACF,eAAO,MAAM,kBAAkB,qDAE9B,CAAC;AAoDF,eAAO,MAAM,0BAA0B,mJA+HpC,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Credentials } from "@distilled.cloud/gcp";
|
|
2
|
+
import { Resource } from "alchemy";
|
|
3
|
+
import { Unowned } from "alchemy/AdoptPolicy";
|
|
4
|
+
import { isResolved, somePropsAreDifferent } from "alchemy/Diff";
|
|
5
|
+
import * as Provider from "alchemy/Provider";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Redacted from "effect/Redacted";
|
|
8
|
+
import { gcpInternalLabels, hasAlchemyLabels } from "../Tags.js";
|
|
9
|
+
import { applyObject, deleteObject, getObject, KubernetesApiError, } from "./client.js";
|
|
10
|
+
export const KubernetesManifest = Resource("GCP.KubernetesManifest");
|
|
11
|
+
const toAttributes = (o, parent) => ({
|
|
12
|
+
apiVersion: o?.apiVersion ?? parent.apiVersion,
|
|
13
|
+
kind: o?.kind ?? parent.kind,
|
|
14
|
+
name: o?.metadata?.name ?? parent.name,
|
|
15
|
+
namespace: o?.metadata?.namespace ?? parent.namespace,
|
|
16
|
+
uid: o?.metadata?.uid,
|
|
17
|
+
resourceVersion: o?.metadata?.resourceVersion,
|
|
18
|
+
endpoint: parent.endpoint,
|
|
19
|
+
caCertificate: parent.caCertificate,
|
|
20
|
+
});
|
|
21
|
+
/** Mint a fresh GKE connection (plain bearer token) from props + ADC creds. */
|
|
22
|
+
const connect = Effect.fn("k8sManifest.connect")(function* (props) {
|
|
23
|
+
const { accessToken } = yield* yield* Credentials;
|
|
24
|
+
return {
|
|
25
|
+
endpoint: props.endpoint,
|
|
26
|
+
caCertificate: props.caCertificate,
|
|
27
|
+
token: Redacted.value(accessToken),
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
/** GET the object, mapping a 404 to `undefined`. */
|
|
31
|
+
const observe = (connection, apiVersion, kind, namespace, name) => getObject(connection, apiVersion, kind, namespace, name).pipe(Effect.catchIf((e) => e instanceof KubernetesApiError && e.statusCode === 404, () => Effect.succeed(undefined)));
|
|
32
|
+
export const KubernetesManifestProvider = () => Provider.effect(KubernetesManifest, Effect.gen(function* () {
|
|
33
|
+
return {
|
|
34
|
+
// Identity (apiVersion/kind/name/namespace) and the server-assigned uid
|
|
35
|
+
// are unchanged by an in-place apply. endpoint is a replace trigger so
|
|
36
|
+
// it never changes on update; caCertificate is excluded (can rotate).
|
|
37
|
+
stables: [
|
|
38
|
+
"apiVersion",
|
|
39
|
+
"kind",
|
|
40
|
+
"name",
|
|
41
|
+
"namespace",
|
|
42
|
+
"uid",
|
|
43
|
+
"endpoint",
|
|
44
|
+
],
|
|
45
|
+
diff: Effect.fn(function* ({ news, olds = {} }) {
|
|
46
|
+
if (!isResolved(news))
|
|
47
|
+
return undefined;
|
|
48
|
+
const o = olds;
|
|
49
|
+
// apiVersion/kind/name/namespace are the object's identity, and
|
|
50
|
+
// endpoint points at a specific cluster — any change orphans the old
|
|
51
|
+
// object, so create new + delete old.
|
|
52
|
+
if (somePropsAreDifferent(o, news, [
|
|
53
|
+
"apiVersion",
|
|
54
|
+
"kind",
|
|
55
|
+
"name",
|
|
56
|
+
"namespace",
|
|
57
|
+
"endpoint",
|
|
58
|
+
])) {
|
|
59
|
+
return { action: "replace" };
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}),
|
|
63
|
+
reconcile: Effect.fn(function* ({ id, news }) {
|
|
64
|
+
const labels = {
|
|
65
|
+
...(news.labels ?? {}),
|
|
66
|
+
...(yield* gcpInternalLabels(id)),
|
|
67
|
+
};
|
|
68
|
+
const connection = yield* connect(news);
|
|
69
|
+
// Build the full object: identity + merged labels + the user body.
|
|
70
|
+
// SSA is declarative — we send exactly what we manage (no
|
|
71
|
+
// resourceVersion), and force ownership of any field we name.
|
|
72
|
+
const object = {
|
|
73
|
+
...news.body,
|
|
74
|
+
apiVersion: news.apiVersion,
|
|
75
|
+
kind: news.kind,
|
|
76
|
+
metadata: {
|
|
77
|
+
name: news.name,
|
|
78
|
+
...(news.namespace ? { namespace: news.namespace } : {}),
|
|
79
|
+
labels,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
const applied = yield* applyObject(connection, object);
|
|
83
|
+
// A 2xx apply normally echoes the object, but an empty-body success is
|
|
84
|
+
// possible — re-read so we always return real attributes (uid/
|
|
85
|
+
// resourceVersion). Use getObject (not observe): a missing object
|
|
86
|
+
// after a successful apply is a real failure, not "absent".
|
|
87
|
+
const final = applied?.metadata != null
|
|
88
|
+
? applied
|
|
89
|
+
: yield* getObject(connection, news.apiVersion, news.kind, news.namespace, news.name);
|
|
90
|
+
return toAttributes(final, {
|
|
91
|
+
apiVersion: news.apiVersion,
|
|
92
|
+
kind: news.kind,
|
|
93
|
+
name: news.name,
|
|
94
|
+
namespace: news.namespace,
|
|
95
|
+
endpoint: news.endpoint,
|
|
96
|
+
caCertificate: news.caCertificate,
|
|
97
|
+
});
|
|
98
|
+
}),
|
|
99
|
+
delete: Effect.fn(function* ({ output }) {
|
|
100
|
+
const connection = yield* connect(output);
|
|
101
|
+
yield* deleteObject(connection, output.apiVersion, output.kind, output.namespace, output.name);
|
|
102
|
+
}),
|
|
103
|
+
read: Effect.fn(function* ({ id, output, olds }) {
|
|
104
|
+
const endpoint = output?.endpoint ?? olds?.endpoint;
|
|
105
|
+
const caCertificate = output?.caCertificate ?? olds?.caCertificate;
|
|
106
|
+
const apiVersion = output?.apiVersion ?? olds?.apiVersion;
|
|
107
|
+
const kind = output?.kind ?? olds?.kind;
|
|
108
|
+
const name = output?.name ?? olds?.name;
|
|
109
|
+
const namespace = output?.namespace ?? olds?.namespace;
|
|
110
|
+
if (!endpoint || !caCertificate || !apiVersion || !kind || !name) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const connection = yield* connect({ endpoint, caCertificate });
|
|
114
|
+
const observed = yield* observe(connection, apiVersion, kind, namespace, name);
|
|
115
|
+
if (!observed)
|
|
116
|
+
return undefined;
|
|
117
|
+
const attrs = toAttributes(observed, {
|
|
118
|
+
apiVersion,
|
|
119
|
+
kind,
|
|
120
|
+
name,
|
|
121
|
+
namespace,
|
|
122
|
+
endpoint,
|
|
123
|
+
caCertificate,
|
|
124
|
+
});
|
|
125
|
+
return (yield* hasAlchemyLabels(id, observed.metadata?.labels))
|
|
126
|
+
? attrs
|
|
127
|
+
: Unowned(attrs);
|
|
128
|
+
}),
|
|
129
|
+
};
|
|
130
|
+
}));
|
|
131
|
+
//# sourceMappingURL=KubernetesManifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KubernetesManifest.js","sourceRoot":"","sources":["../../src/Kubernetes/KubernetesManifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,23 +6,31 @@ import type * as GCP from "../Providers.ts";
|
|
|
6
6
|
/**
|
|
7
7
|
* An Opaque Kubernetes Secret in a GKE cluster.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* Driven through the typed `@distilled.cloud/kubernetes` SDK (core/v1) rather
|
|
10
|
+
* than alchemy's upstream Kubernetes provider, which is EKS-only. The
|
|
11
|
+
* control-plane connection (`endpoint` + `caCertificate`) comes from a
|
|
12
|
+
* {@link GCP.Cluster}'s attributes; the bearer token is minted from the
|
|
13
|
+
* provider's ADC {@link Credentials} and threaded into the cluster via
|
|
14
|
+
* {@link clusterLayer} (which also trusts the per-cluster CA).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
16
|
+
* Convergence is a typed **create-or-replace upsert** (not server-side apply):
|
|
17
|
+
* `reconcile` reads the Secret, then `createCoreV1NamespacedSecret` if absent
|
|
18
|
+
* or `replaceCoreV1NamespacedSecret` (a full PUT, carrying the observed
|
|
19
|
+
* `resourceVersion` for optimistic concurrency) if present. A `PUT` is a full
|
|
20
|
+
* object replacement, so dropping a key from `data` removes it from the stored
|
|
21
|
+
* Secret — the same pruning SSA gave us, without needing the
|
|
22
|
+
* `application/apply-patch+yaml` content type the typed PATCH op can't express.
|
|
23
|
+
*
|
|
24
|
+
* Ownership for adoption is gated on alchemy-internal `metadata.labels` (same
|
|
25
|
+
* triple as labelled GCP resources), so a Secret created out of band reads back
|
|
26
|
+
* as {@link Unowned} until adopted.
|
|
19
27
|
*
|
|
20
28
|
* @section Creating a Secret
|
|
21
29
|
* @example Wire a Cloudflare tunnel token into the cluster
|
|
22
30
|
* ```typescript
|
|
23
31
|
* // `tunnel.token` is a `Redacted<string>`; pass it straight through —
|
|
24
|
-
* // the value is unwrapped only at the moment it's written to the API,
|
|
25
|
-
* //
|
|
32
|
+
* // the value is unwrapped only at the moment it's written to the API, and
|
|
33
|
+
* // stays redacted in logs/plan output.
|
|
26
34
|
* yield* GCP.KubernetesSecret("CloudflaredTunnelSecret", {
|
|
27
35
|
* endpoint: cluster.endpoint,
|
|
28
36
|
* caCertificate: cluster.clusterCaCertificate,
|
|
@@ -53,15 +61,14 @@ export type KubernetesSecretProps = {
|
|
|
53
61
|
/** Secret `type`. Default `"Opaque"`. Mutable. */
|
|
54
62
|
type?: string;
|
|
55
63
|
/**
|
|
56
|
-
* String data (
|
|
64
|
+
* String data (UTF-8 values, base64-encoded on the way to the API). Mutable.
|
|
57
65
|
*
|
|
58
|
-
* Values may be `Redacted<string>` (e.g. a resource's secret output
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* are accepted too.
|
|
66
|
+
* Values may be `Redacted<string>` (e.g. a resource's secret output like
|
|
67
|
+
* `tunnel.token`) — they are unwrapped only when written to the Kubernetes
|
|
68
|
+
* API and stay opaque in logs/plan output. Plain strings are accepted too.
|
|
62
69
|
*
|
|
63
|
-
* NOTE: the underlying values are persisted in alchemy stack state
|
|
64
|
-
*
|
|
70
|
+
* NOTE: the underlying values are persisted in alchemy stack state (the
|
|
71
|
+
* engine diffs on the real value) — protect the state backend.
|
|
65
72
|
*/
|
|
66
73
|
stringData: Record<string, Redacted.Redacted<string> | string>;
|
|
67
74
|
/** Extra metadata labels (alchemy ownership labels merge on top). Mutable. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secret.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/Secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"Secret.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/Secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAOnD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAInC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAI5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;OASG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/D,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,qDAAqD;IACrD,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CACrC,sBAAsB,EACtB,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,EACL,GAAG,CAAC,SAAS,CACd,CAAC;AACF,eAAO,MAAM,gBAAgB,mDAE5B,CAAC;AAuEF,eAAO,MAAM,wBAAwB,iJAgKlC,CAAC"}
|
package/lib/Kubernetes/Secret.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Credentials } from "@distilled.cloud/gcp";
|
|
2
|
+
import { createCoreV1NamespacedSecret, deleteCoreV1NamespacedSecret, readCoreV1NamespacedSecret, replaceCoreV1NamespacedSecret, } from "@distilled.cloud/kubernetes/core";
|
|
2
3
|
import { Resource } from "alchemy";
|
|
3
4
|
import { Unowned } from "alchemy/AdoptPolicy";
|
|
4
5
|
import { isResolved, somePropsAreDifferent } from "alchemy/Diff";
|
|
@@ -7,7 +8,7 @@ import * as Provider from "alchemy/Provider";
|
|
|
7
8
|
import * as Effect from "effect/Effect";
|
|
8
9
|
import * as Redacted from "effect/Redacted";
|
|
9
10
|
import { gcpInternalLabels, hasAlchemyLabels } from "../Tags.js";
|
|
10
|
-
import {
|
|
11
|
+
import { clusterLayer } from "./connection.js";
|
|
11
12
|
export const KubernetesSecret = Resource("GCP.KubernetesSecret");
|
|
12
13
|
const toAttributes = (s, parent) => ({
|
|
13
14
|
name: s?.metadata?.name ?? parent.name,
|
|
@@ -24,37 +25,46 @@ const connect = Effect.fn("k8sSecret.connect")(function* (props) {
|
|
|
24
25
|
return {
|
|
25
26
|
endpoint: props.endpoint,
|
|
26
27
|
caCertificate: props.caCertificate,
|
|
27
|
-
token:
|
|
28
|
+
token: accessToken,
|
|
28
29
|
};
|
|
29
30
|
});
|
|
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
|
+
};
|
|
31
42
|
export const KubernetesSecretProvider = () => Provider.effect(KubernetesSecret, Effect.gen(function* () {
|
|
32
43
|
return {
|
|
33
|
-
// Attributes unchanged by an in-place update (so dependents can
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
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.
|
|
38
49
|
stables: ["name", "namespace", "endpoint", "uid"],
|
|
39
50
|
diff: Effect.fn(function* ({ id, news, olds = {} }) {
|
|
40
51
|
if (!isResolved(news))
|
|
41
52
|
return undefined;
|
|
42
53
|
const o = olds;
|
|
43
|
-
// Compare the RESOLVED name and type (with their defaults
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// replace.
|
|
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.
|
|
48
58
|
const defaultName = (yield* createPhysicalName({ id, maxLength: 63 })).toLowerCase();
|
|
49
59
|
const sameName = (o.name ?? defaultName) === (news.name ?? defaultName);
|
|
50
60
|
const sameType = (o.type ?? "Opaque") === (news.type ?? "Opaque");
|
|
51
|
-
// Replace (create new + delete old) rather than in-place
|
|
52
|
-
// - `endpoint`: points at a different cluster (would orphan the
|
|
53
|
-
//
|
|
54
|
-
//
|
|
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.
|
|
55
65
|
// - `namespace`/`name`: the object's identity.
|
|
56
|
-
// - `type`: immutable on a Kubernetes Secret;
|
|
57
|
-
//
|
|
66
|
+
// - `type`: immutable on a Kubernetes Secret; a PUT with a new type
|
|
67
|
+
// is rejected by the API.
|
|
58
68
|
if (!sameName ||
|
|
59
69
|
!sameType ||
|
|
60
70
|
somePropsAreDifferent(o, news, ["namespace", "endpoint"])) {
|
|
@@ -71,33 +81,52 @@ export const KubernetesSecretProvider = () => Provider.effect(KubernetesSecret,
|
|
|
71
81
|
...(yield* gcpInternalLabels(id)),
|
|
72
82
|
};
|
|
73
83
|
const connection = yield* connect(news);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
+
},
|
|
88
108
|
type: desiredType,
|
|
89
109
|
data,
|
|
90
|
-
});
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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, {
|
|
101
130
|
name: desiredName,
|
|
102
131
|
namespace: news.namespace,
|
|
103
132
|
endpoint: news.endpoint,
|
|
@@ -107,7 +136,12 @@ export const KubernetesSecretProvider = () => Provider.effect(KubernetesSecret,
|
|
|
107
136
|
}),
|
|
108
137
|
delete: Effect.fn(function* ({ output }) {
|
|
109
138
|
const connection = yield* connect(output);
|
|
110
|
-
yield*
|
|
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));
|
|
111
145
|
}),
|
|
112
146
|
read: Effect.fn(function* ({ id, output, olds }) {
|
|
113
147
|
const endpoint = output?.endpoint ?? olds?.endpoint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Secret.js","sourceRoot":"","sources":["../../src/Kubernetes/Secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,
|
|
1
|
+
{"version":3,"file":"Secret.js","sourceRoot":"","sources":["../../src/Kubernetes/Secret.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,8 +19,8 @@ export declare class KubernetesApiError extends KubernetesApiError_base<{
|
|
|
19
19
|
body: string;
|
|
20
20
|
}> {
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
export interface
|
|
22
|
+
/** A Kubernetes object as seen on the wire (only the fields we read back). */
|
|
23
|
+
export interface KubeObject {
|
|
24
24
|
apiVersion?: string;
|
|
25
25
|
kind?: string;
|
|
26
26
|
metadata?: {
|
|
@@ -29,30 +29,34 @@ export interface SecretObject {
|
|
|
29
29
|
uid?: string;
|
|
30
30
|
resourceVersion?: string;
|
|
31
31
|
labels?: Record<string, string>;
|
|
32
|
+
[key: string]: unknown;
|
|
32
33
|
};
|
|
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;
|
|
34
42
|
}
|
|
35
|
-
/** GET a Secret; the caller maps `KubernetesApiError(404)` to "missing". */
|
|
36
|
-
export declare const getSecret: (connection: GkeConnection, namespace: string, name: string) => Effect.Effect<SecretObject, KubernetesApiError>;
|
|
37
43
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* Values go through `data` (base64), NOT `stringData`: server-side apply
|
|
42
|
-
* tracks the field manager's ownership of `data` keys, so dropping a key
|
|
43
|
-
* from a later apply prunes it from the stored Secret. `stringData` is
|
|
44
|
-
* write-only/ephemeral and isn't tracked, which would leave stale keys.
|
|
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.
|
|
45
47
|
*/
|
|
46
|
-
export declare const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
export declare const
|
|
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>;
|
|
57
61
|
export {};
|
|
58
62
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/Kubernetes/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;
|
|
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"}
|