@microagi/alchemy-gcp 0.8.0 → 0.9.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 (55) hide show
  1. package/lib/Container/NodePool.d.ts +10 -0
  2. package/lib/Container/NodePool.d.ts.map +1 -1
  3. package/lib/Container/NodePool.js +4 -0
  4. package/lib/Container/NodePool.js.map +1 -1
  5. package/lib/Iam/ServiceAccount.d.ts.map +1 -1
  6. package/lib/Iam/ServiceAccount.js +21 -2
  7. package/lib/Iam/ServiceAccount.js.map +1 -1
  8. package/lib/Iam/ServiceAccountKey.d.ts +72 -0
  9. package/lib/Iam/ServiceAccountKey.d.ts.map +1 -0
  10. package/lib/Iam/ServiceAccountKey.js +164 -0
  11. package/lib/Iam/ServiceAccountKey.js.map +1 -0
  12. package/lib/Iam/index.d.ts +2 -0
  13. package/lib/Iam/index.d.ts.map +1 -1
  14. package/lib/Iam/index.js +1 -0
  15. package/lib/Iam/index.js.map +1 -1
  16. package/lib/Kubernetes/Helm.d.ts +132 -0
  17. package/lib/Kubernetes/Helm.d.ts.map +1 -0
  18. package/lib/Kubernetes/Helm.js +314 -0
  19. package/lib/Kubernetes/Helm.js.map +1 -0
  20. package/lib/Kubernetes/index.d.ts +1 -0
  21. package/lib/Kubernetes/index.d.ts.map +1 -1
  22. package/lib/Kubernetes/index.js +1 -0
  23. package/lib/Kubernetes/index.js.map +1 -1
  24. package/lib/Providers.d.ts +1 -1
  25. package/lib/Providers.d.ts.map +1 -1
  26. package/lib/Providers.js +7 -1
  27. package/lib/Providers.js.map +1 -1
  28. package/lib/Storage/Bucket.d.ts +117 -0
  29. package/lib/Storage/Bucket.d.ts.map +1 -0
  30. package/lib/Storage/Bucket.js +239 -0
  31. package/lib/Storage/Bucket.js.map +1 -0
  32. package/lib/Storage/BucketIamMember.d.ts +29 -0
  33. package/lib/Storage/BucketIamMember.d.ts.map +1 -0
  34. package/lib/Storage/BucketIamMember.js +26 -0
  35. package/lib/Storage/BucketIamMember.js.map +1 -0
  36. package/lib/Storage/index.d.ts +4 -0
  37. package/lib/Storage/index.d.ts.map +1 -0
  38. package/lib/Storage/index.js +3 -0
  39. package/lib/Storage/index.js.map +1 -0
  40. package/lib/index.d.ts +1 -0
  41. package/lib/index.d.ts.map +1 -1
  42. package/lib/index.js +1 -0
  43. package/lib/index.js.map +1 -1
  44. package/package.json +2 -2
  45. package/src/Container/NodePool.ts +12 -0
  46. package/src/Iam/ServiceAccount.ts +21 -2
  47. package/src/Iam/ServiceAccountKey.ts +284 -0
  48. package/src/Iam/index.ts +5 -0
  49. package/src/Kubernetes/Helm.ts +536 -0
  50. package/src/Kubernetes/index.ts +1 -0
  51. package/src/Providers.ts +18 -0
  52. package/src/Storage/Bucket.ts +439 -0
  53. package/src/Storage/BucketIamMember.ts +33 -0
  54. package/src/Storage/index.ts +10 -0
  55. package/src/index.ts +1 -0
@@ -0,0 +1,117 @@
1
+ import * as storage from "@distilled.cloud/gcp/storage-v1";
2
+ import { Resource } from "alchemy";
3
+ import * as Provider from "alchemy/Provider";
4
+ import type * as GCP from "../Providers.ts";
5
+ /**
6
+ * A GCS bucket. Lives under a
7
+ * {@link import("../CloudResourceManager/Project.ts").Project} and serves
8
+ * as the target for {@link storageBucketIamMember} bindings (bucket-level
9
+ * IAM grants merged into a single `setIamPolicy` call).
10
+ *
11
+ * Adoption is gated on alchemy internal labels (`alchemy_app` /
12
+ * `alchemy_stage` / `alchemy_id`) — GCS buckets support labels, so the
13
+ * same label-based adoption gate as `Project` applies. The labels are
14
+ * merged into the user-supplied label set on create and never cleared.
15
+ *
16
+ * All lifecycle operations (`insertBuckets`, `getBuckets`, `patchBuckets`,
17
+ * `deleteBuckets`) are synchronous — no LRO polling required.
18
+ *
19
+ * @section Creating a StorageBucket
20
+ * @example Bucket with a 48-hour object TTL
21
+ * ```typescript
22
+ * const bucket = yield* GCP.StorageBucket("TempBucket", {
23
+ * project: project.projectId,
24
+ * name: "microagi-shared-temp",
25
+ * location: "europe-west4",
26
+ * storageClass: "STANDARD",
27
+ * lifecycle: {
28
+ * rule: [{
29
+ * action: { type: "Delete" },
30
+ * condition: { age: 2 }, // 2 days = 48 hours
31
+ * }],
32
+ * },
33
+ * });
34
+ * ```
35
+ */
36
+ export type StorageBucketProps = {
37
+ /**
38
+ * GCP project id (e.g. `micro-research-shared`) the bucket lives
39
+ * under. Immutable — changing this orphans the bucket and its IAM
40
+ * bindings, so `diff` triggers a replacement.
41
+ */
42
+ project: string;
43
+ /**
44
+ * The bucket name. Must be globally unique, 3-222 chars, lowercase
45
+ * letters, digits, hyphens, underscores, and dots. Immutable —
46
+ * replaces.
47
+ */
48
+ name: string;
49
+ /**
50
+ * Bucket location, e.g. `europe-west4` (single region) or `EU`
51
+ * (multi-region). Immutable — replaces.
52
+ */
53
+ location: string;
54
+ /**
55
+ * Storage class, e.g. `STANDARD`, `NEARLINE`, `COLDLINE`,
56
+ * `ARCHIVE`. Defaults to `STANDARD` server-side. Mutable.
57
+ */
58
+ storageClass?: string;
59
+ /**
60
+ * Bucket lifecycle configuration. Used for object TTL (e.g.
61
+ * `{ rule: [{ action: { type: "Delete" }, condition: { age: 2 } }] }`
62
+ * deletes objects older than 2 days). Mutable.
63
+ */
64
+ lifecycle?: storage.Bucket["lifecycle"];
65
+ /**
66
+ * User-supplied labels. The alchemy internal labels are merged
67
+ * automatically (see `gcpInternalLabels`). Alchemy keys are reserved
68
+ * and overwrite user-supplied collisions.
69
+ */
70
+ labels?: Record<string, string>;
71
+ /**
72
+ * Whether uniform bucket-level access is enabled. Defaults to
73
+ * `true` (recommended — ACLs are disabled, IAM is the sole authz
74
+ * path). Mutable.
75
+ */
76
+ uniformBucketLevelAccess?: boolean;
77
+ };
78
+ /**
79
+ * Single (role, members) entry on a bucket's IAM policy. Targets
80
+ * declare this contract so {@link storageBucketIamMember} can `.bind`
81
+ * IAM grants onto the bucket — the provider's `reconcile` merges all
82
+ * bound entries by role into a single `setIamPolicy` call.
83
+ */
84
+ export type StorageBucketIamBinding = {
85
+ /** IAM role, e.g. `"roles/storage.objectUser"`. */
86
+ role: string;
87
+ /** Principals, e.g. `["serviceAccount:sa@project.iam.gserviceaccount.com"]`. */
88
+ members: ReadonlyArray<string>;
89
+ };
90
+ export type StorageBucketBindingContract = {
91
+ iamBindings: ReadonlyArray<StorageBucketIamBinding>;
92
+ };
93
+ export type StorageBucket = Resource<"GCP.StorageBucket", StorageBucketProps, {
94
+ /** Bucket name (same as the prop). */
95
+ name: string;
96
+ /** Bucket ID (server-assigned, same as name for buckets). */
97
+ id: string;
98
+ /** Self-link URL. */
99
+ selfLink: string;
100
+ /** Bucket location. */
101
+ location: string;
102
+ /** Storage class. */
103
+ storageClass: string | undefined;
104
+ /** Lifecycle configuration. */
105
+ lifecycle: storage.Bucket["lifecycle"];
106
+ /** Labels (including alchemy internal labels). */
107
+ labels: Record<string, string> | undefined;
108
+ /** Metageneration (changes on metadata updates). */
109
+ metageneration: string;
110
+ /** Creation time (RFC 3339). */
111
+ timeCreated: string;
112
+ /** Last update time (RFC 3339). */
113
+ updated: string;
114
+ }, StorageBucketBindingContract, GCP.Providers>;
115
+ export declare const StorageBucket: import("alchemy").ResourceClass<StorageBucket>;
116
+ export declare const StorageBucketProvider: () => import("effect/Layer").Layer<Provider.Provider<StorageBucket>, never, import("alchemy").Stack | import("alchemy").Stage | import("@distilled.cloud/gcp").Credentials | import("effect/unstable/http/HttpClient").HttpClient>;
117
+ //# sourceMappingURL=Bucket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bucket.d.ts","sourceRoot":"","sources":["../../src/Storage/Bucket.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,OAAO,MAAM,iCAAiC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAS7C,OAAO,KAAK,KAAK,GAAG,MAAM,iBAAiB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC;;;;OAIG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,WAAW,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;CACrD,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,QAAQ,CAClC,mBAAmB,EACnB,kBAAkB,EAClB;IACE,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+BAA+B;IAC/B,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC3C,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;CACjB,EACD,4BAA4B,EAC5B,GAAG,CAAC,SAAS,CACd,CAAC;AAEF,eAAO,MAAM,aAAa,gDAA+C,CAAC;AAe1E,eAAO,MAAM,qBAAqB,oOA+R/B,CAAC"}
@@ -0,0 +1,239 @@
1
+ import { ConfigError } from "@distilled.cloud/gcp";
2
+ import * as storage from "@distilled.cloud/gcp/storage-v1";
3
+ import { Resource } from "alchemy";
4
+ import { Unowned } from "alchemy/AdoptPolicy";
5
+ import { isResolved } from "alchemy/Diff";
6
+ import * as Provider from "alchemy/Provider";
7
+ import * as Duration from "effect/Duration";
8
+ import * as Effect from "effect/Effect";
9
+ import * as Schedule from "effect/Schedule";
10
+ import { gcpInternalLabels, hasAlchemyLabels, } from "../Tags.js";
11
+ export const StorageBucket = Resource("GCP.StorageBucket");
12
+ const toAttributes = (b) => ({
13
+ name: b.name ?? "",
14
+ id: b.id ?? b.name ?? "",
15
+ selfLink: b.selfLink ?? "",
16
+ location: b.location ?? "",
17
+ storageClass: b.storageClass,
18
+ lifecycle: b.lifecycle,
19
+ labels: b.labels,
20
+ metageneration: b.metageneration ?? "",
21
+ timeCreated: b.timeCreated ?? "",
22
+ updated: b.updated ?? "",
23
+ });
24
+ export const StorageBucketProvider = () => Provider.effect(StorageBucket, Effect.gen(function* () {
25
+ const getBuckets = yield* storage.getBuckets;
26
+ const insertBuckets = yield* storage.insertBuckets;
27
+ const patchBuckets = yield* storage.patchBuckets;
28
+ const deleteBuckets = yield* storage.deleteBuckets;
29
+ const listBuckets = yield* storage.listBuckets;
30
+ const getIamPolicyBuckets = yield* storage.getIamPolicyBuckets;
31
+ const setIamPolicyBuckets = yield* storage.setIamPolicyBuckets;
32
+ const observeBucket = (name) => getBuckets({ bucket: name }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)), Effect.catchTag("Forbidden", () => Effect.succeed(undefined)));
33
+ const requireBucket = (name, context) => observeBucket(name).pipe(Effect.flatMap((b) => b
34
+ ? Effect.succeed(b)
35
+ : Effect.fail(new ConfigError({
36
+ message: `Bucket ${name} ${context}.`,
37
+ }))));
38
+ // List-scan for cold recovery. Alchemy labels on the bucket
39
+ // identify our resources; the prefix narrows the scan.
40
+ const findByAlchemyLabels = Effect.fn(function* (id, project, name) {
41
+ let pageToken;
42
+ for (let i = 0; i < 100; i++) {
43
+ const page = yield* listBuckets({
44
+ project,
45
+ prefix: name.slice(0, Math.min(name.length, 10)),
46
+ maxResults: 100,
47
+ ...(pageToken ? { pageToken } : {}),
48
+ });
49
+ for (const b of page.items ?? []) {
50
+ if (b.name === name && (yield* hasAlchemyLabels(id, b.labels))) {
51
+ return b;
52
+ }
53
+ }
54
+ pageToken = page.nextPageToken;
55
+ if (!pageToken)
56
+ return undefined;
57
+ }
58
+ return undefined;
59
+ });
60
+ // Sync mutable fields: storageClass, lifecycle, labels,
61
+ // uniformBucketLevelAccess. Uses PATCH with the full bucket body
62
+ // (GCS patch semantics merge supplied fields). Diff against
63
+ // OBSERVED state, not `olds`, so adoption converges correctly.
64
+ const syncMutable = Effect.fn(function* (observed, desired) {
65
+ const needsUpdate = desired.storageClass !== observed.storageClass ||
66
+ JSON.stringify(desired.lifecycle) !==
67
+ JSON.stringify(observed.lifecycle) ||
68
+ JSON.stringify(desired.labels) !==
69
+ JSON.stringify(observed.labels ?? {}) ||
70
+ desired.uniformBucketLevelAccess !==
71
+ (observed.iamConfiguration?.uniformBucketLevelAccess?.enabled ?? false);
72
+ if (!needsUpdate)
73
+ return observed;
74
+ return yield* patchBuckets({
75
+ bucket: observed.name,
76
+ body: {
77
+ storageClass: desired.storageClass,
78
+ lifecycle: desired.lifecycle,
79
+ labels: desired.labels,
80
+ iamConfiguration: {
81
+ uniformBucketLevelAccess: {
82
+ enabled: desired.uniformBucketLevelAccess,
83
+ },
84
+ },
85
+ },
86
+ });
87
+ });
88
+ // Apply merged IAM bindings as a single setIamPolicy. Same
89
+ // foreign-binding preservation + etag-retry pattern as
90
+ // ServiceAccount's `syncIam`.
91
+ const syncIam = (args) => Effect.gen(function* () {
92
+ const desiredByRole = new Map();
93
+ for (const b of args.bindings) {
94
+ for (const ib of b.data.iamBindings) {
95
+ const set = desiredByRole.get(ib.role) ?? new Set();
96
+ for (const m of ib.members)
97
+ set.add(m);
98
+ desiredByRole.set(ib.role, set);
99
+ }
100
+ }
101
+ if (desiredByRole.size === 0)
102
+ return;
103
+ const current = yield* getIamPolicyBuckets({
104
+ bucket: args.bucketName,
105
+ optionsRequestedPolicyVersion: 3,
106
+ });
107
+ const existingBindings = (current.bindings ?? []).map((b) => ({
108
+ ...b,
109
+ members: [...(b.members ?? [])],
110
+ }));
111
+ let mutated = false;
112
+ for (const [role, members] of desiredByRole) {
113
+ let existing = existingBindings.find((b) => b.role === role && !b.condition);
114
+ if (!existing) {
115
+ existing = { role, members: [] };
116
+ existingBindings.push(existing);
117
+ }
118
+ const merged = new Set([...(existing.members ?? []), ...members]);
119
+ if (merged.size !== (existing.members?.length ?? 0))
120
+ mutated = true;
121
+ existing.members = [...merged];
122
+ }
123
+ if (!mutated)
124
+ return;
125
+ yield* setIamPolicyBuckets({
126
+ bucket: args.bucketName,
127
+ body: {
128
+ ...current,
129
+ bindings: existingBindings,
130
+ version: 3,
131
+ },
132
+ });
133
+ }).pipe(Effect.retry({
134
+ schedule: Schedule.exponential(Duration.seconds(2)).pipe(Schedule.both(Schedule.recurs(8))),
135
+ }));
136
+ return {
137
+ stables: ["name", "id", "selfLink", "location", "timeCreated"],
138
+ diff: Effect.fn(function* ({ news, olds = {}, output }) {
139
+ if (!isResolved(news))
140
+ return undefined;
141
+ const priorName = output?.name || olds.name;
142
+ const priorProject = olds.project;
143
+ const priorLocation = olds.location;
144
+ if ((priorName && news.name !== priorName) ||
145
+ (priorProject && news.project !== priorProject) ||
146
+ (priorLocation && news.location !== priorLocation)) {
147
+ return { action: "replace" };
148
+ }
149
+ return undefined;
150
+ }),
151
+ reconcile: Effect.fn(function* ({ id, news, bindings, output }) {
152
+ const internalLabels = yield* gcpInternalLabels(id);
153
+ const mergedLabels = { ...(news.labels ?? {}), ...internalLabels };
154
+ let created = false;
155
+ // 1. Observe — cloud state is authoritative. Fall back to
156
+ // label-scan for cold recovery.
157
+ let observed = yield* observeBucket(news.name);
158
+ if (!observed) {
159
+ observed = yield* findByAlchemyLabels(id, news.project, news.name);
160
+ }
161
+ // 2. Ensure — create if missing. insertBuckets is
162
+ // synchronous (returns the bucket, not an LRO). Conflict
163
+ // covers a peer reconciler race — re-observe.
164
+ if (!observed) {
165
+ observed = yield* insertBuckets({
166
+ project: news.project,
167
+ body: {
168
+ name: news.name,
169
+ location: news.location,
170
+ storageClass: news.storageClass,
171
+ lifecycle: news.lifecycle,
172
+ labels: mergedLabels,
173
+ iamConfiguration: {
174
+ uniformBucketLevelAccess: {
175
+ enabled: news.uniformBucketLevelAccess ?? true,
176
+ },
177
+ },
178
+ },
179
+ }).pipe(Effect.catchTag("Conflict", () => Effect.succeed(undefined)));
180
+ created = !!observed;
181
+ if (!observed) {
182
+ observed = yield* requireBucket(news.name, "did not appear after create");
183
+ }
184
+ }
185
+ // Refuse to mutate an existing unowned bucket on greenfield
186
+ // create. The `read` handler returns Unowned(attrs) for
187
+ // buckets without our labels, which gates adoption behind
188
+ // --adopt. But reconcile runs before read on a first deploy,
189
+ // so without this guard we'd sync mutable fields (labels,
190
+ // lifecycle, UBLA) on a foreign bucket — bypassing the
191
+ // adoption gate. Skip the check when we just created the
192
+ // bucket (it has our labels) or when the engine already
193
+ // adopted it (output is defined).
194
+ if (!created &&
195
+ !output &&
196
+ !(yield* hasAlchemyLabels(id, observed.labels))) {
197
+ return yield* Effect.fail(new ConfigError({
198
+ message: `Bucket ${news.name} already exists without matching alchemy labels; use --adopt to take it over.`,
199
+ }));
200
+ }
201
+ // 3. Sync mutable fields against OBSERVED state.
202
+ const synced = yield* syncMutable(observed, {
203
+ storageClass: news.storageClass ?? observed.storageClass,
204
+ lifecycle: news.lifecycle ?? observed.lifecycle,
205
+ labels: mergedLabels,
206
+ uniformBucketLevelAccess: news.uniformBucketLevelAccess ?? true,
207
+ });
208
+ // 4. Sync IAM bindings (storage.objectUser, etc.).
209
+ yield* syncIam({
210
+ bucketName: synced.name,
211
+ bindings,
212
+ });
213
+ return toAttributes(synced);
214
+ }),
215
+ delete: Effect.fn(function* ({ output }) {
216
+ yield* deleteBuckets({
217
+ bucket: output.name,
218
+ }).pipe(Effect.catchTag("NotFound", () => Effect.void), Effect.catchTag("Forbidden", () => Effect.void));
219
+ }),
220
+ read: Effect.fn(function* ({ id, output, olds }) {
221
+ const name = output?.name ?? olds?.name;
222
+ const project = olds?.project;
223
+ let observed;
224
+ if (name) {
225
+ observed = yield* observeBucket(name);
226
+ }
227
+ if (!observed && project) {
228
+ observed = yield* findByAlchemyLabels(id, project, name ?? "");
229
+ }
230
+ if (!observed)
231
+ return undefined;
232
+ const attrs = toAttributes(observed);
233
+ return (yield* hasAlchemyLabels(id, observed.labels))
234
+ ? attrs
235
+ : Unowned(attrs);
236
+ }),
237
+ };
238
+ }));
239
+ //# sourceMappingURL=Bucket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Bucket.js","sourceRoot":"","sources":["../../src/Storage/Bucket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type { StorageBucket } from "./Bucket.ts";
3
+ /**
4
+ * Bind a single `(role, member)` IAM grant onto a
5
+ * {@link StorageBucket}.
6
+ *
7
+ * Target-side binding — same pattern as
8
+ * {@link import("../CloudResourceManager/ProjectIamMember.ts").projectIamMember}
9
+ * / `serviceAccountIamMember`. The bucket's `reconcile` merges all
10
+ * bindings into a single `setIamPolicy` call on the bucket, preserving
11
+ * foreign roles + members.
12
+ *
13
+ * Identity for dedup: the rendered SID is `IamMember(<bucket>, <key>)`.
14
+ * The engine collapses binds by SID, so callers MUST supply a
15
+ * stack-unique `key` per (bucket, role, member) triple.
16
+ *
17
+ * @example Granting a service account read+write on a bucket
18
+ * ```typescript
19
+ * yield* GCP.storageBucketIamMember(bucket, "TempBucketRw", {
20
+ * role: "roles/storage.objectUser",
21
+ * member: `serviceAccount:${sa.email}`,
22
+ * });
23
+ * ```
24
+ */
25
+ export declare const storageBucketIamMember: (bucket: StorageBucket, key: string, args: {
26
+ role: string;
27
+ member: string;
28
+ }) => Effect.Effect<void>;
29
+ //# sourceMappingURL=BucketIamMember.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BucketIamMember.d.ts","sourceRoot":"","sources":["../../src/Storage/BucketIamMember.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,aAAa,EACrB,KAAK,MAAM,EACX,MAAM;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACrC,MAAM,CAAC,MAAM,CAAC,IAAI,CAGiB,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Bind a single `(role, member)` IAM grant onto a
3
+ * {@link StorageBucket}.
4
+ *
5
+ * Target-side binding — same pattern as
6
+ * {@link import("../CloudResourceManager/ProjectIamMember.ts").projectIamMember}
7
+ * / `serviceAccountIamMember`. The bucket's `reconcile` merges all
8
+ * bindings into a single `setIamPolicy` call on the bucket, preserving
9
+ * foreign roles + members.
10
+ *
11
+ * Identity for dedup: the rendered SID is `IamMember(<bucket>, <key>)`.
12
+ * The engine collapses binds by SID, so callers MUST supply a
13
+ * stack-unique `key` per (bucket, role, member) triple.
14
+ *
15
+ * @example Granting a service account read+write on a bucket
16
+ * ```typescript
17
+ * yield* GCP.storageBucketIamMember(bucket, "TempBucketRw", {
18
+ * role: "roles/storage.objectUser",
19
+ * member: `serviceAccount:${sa.email}`,
20
+ * });
21
+ * ```
22
+ */
23
+ export const storageBucketIamMember = (bucket, key, args) => bucket.bind `IamMember(${bucket}, ${key})`({
24
+ iamBindings: [{ role: args.role, members: [args.member] }],
25
+ });
26
+ //# sourceMappingURL=BucketIamMember.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BucketIamMember.js","sourceRoot":"","sources":["../../src/Storage/BucketIamMember.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,MAAqB,EACrB,GAAW,EACX,IAAsC,EACjB,EAAE,CACvB,MAAM,CAAC,IAAI,CAAA,aAAa,MAAM,KAAK,GAAG,GAAG,CAAC;IACxC,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;CAC3D,CAAmC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { StorageBucket, StorageBucketProvider, } from "./Bucket.ts";
2
+ export type { StorageBucketProps, StorageBucketIamBinding, StorageBucketBindingContract, } from "./Bucket.ts";
3
+ export * from "./BucketIamMember.ts";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,kBAAkB,EAClB,uBAAuB,EACvB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,cAAc,sBAAsB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { StorageBucket, StorageBucketProvider, } from "./Bucket.js";
2
+ export * from "./BucketIamMember.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAMrB,cAAc,sBAAsB,CAAC"}
package/lib/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export * from "./Run/index.ts";
12
12
  export * from "./ServiceNetworking/index.ts";
13
13
  export * from "./Sqladmin/index.ts";
14
14
  export * from "./ServiceUsage/index.ts";
15
+ export * from "./Storage/index.ts";
15
16
  export * from "./Providers.ts";
16
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,OAAO,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,aAAa,EACb,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,OAAO,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,aAAa,EACb,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAChC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC"}
package/lib/index.js CHANGED
@@ -11,5 +11,6 @@ export * from "./Run/index.js";
11
11
  export * from "./ServiceNetworking/index.js";
12
12
  export * from "./Sqladmin/index.js";
13
13
  export * from "./ServiceUsage/index.js";
14
+ export * from "./Storage/index.js";
14
15
  export * from "./Providers.js";
15
16
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,OAAO,GACR,MAAM,wBAAwB,CAAC;AAKhC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EACL,sBAAsB,EACtB,OAAO,GACR,MAAM,wBAAwB,CAAC;AAKhC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@microagi/alchemy-gcp",
3
- "version": "0.8.0",
4
- "description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, ServiceAccount, Cluster, NodePool, Compute, ManagedLustre, ServiceUsage, ServiceNetworking, Cloud Run (Service + Job + IAM), Cloud SQL (Instance + Database + User with IAM database authn), Artifact Registry (Docker repositories, standard + remote pull-through caches), Kubernetes (typed Secret + generic SSA Manifest), with ADC-backed credentials and target-side IAM bindings.",
3
+ "version": "0.9.0",
4
+ "description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, ServiceAccount, Cluster, NodePool, Compute, ManagedLustre, ServiceUsage, ServiceNetworking, Cloud Run (Service + Job + IAM), Cloud SQL (Instance + Database + User with IAM database authn), Artifact Registry (Docker repositories, standard + remote pull-through caches), Kubernetes (typed Secret + generic SSA Manifest + Helm release), with ADC-backed credentials and target-side IAM bindings.",
5
5
  "keywords": [
6
6
  "alchemy",
7
7
  "alchemy-effect",
@@ -144,6 +144,14 @@ export type NodePoolProps = {
144
144
  gvnic?: { enabled: boolean };
145
145
  /** Confidential VM. Mutable via `update`. */
146
146
  confidentialNodes?: { enabled: boolean };
147
+ /**
148
+ * GCFS (Image Streaming) — stream container image data from Artifact
149
+ * Registry so containers start before the full image is pulled,
150
+ * cutting cold start for large images. Only accelerates images stored
151
+ * in Artifact Registry. Mutable via `update`. Enabling/disabling on an
152
+ * existing pool re-creates its nodes (rolling node recreation).
153
+ */
154
+ gcfsConfig?: { enabled: boolean };
147
155
  };
148
156
  /** Cluster autoscaler. Mutable via `setAutoscaling`. */
149
157
  autoscaling?: {
@@ -295,6 +303,7 @@ export const toNodeConfigCreateBody = (
295
303
  : {}),
296
304
  ...(config.gvnic ? { gvnic: config.gvnic } : {}),
297
305
  ...(config.confidentialNodes ? { confidentialNodes: config.confidentialNodes } : {}),
306
+ ...(config.gcfsConfig ? { gcfsConfig: config.gcfsConfig } : {}),
298
307
  };
299
308
  };
300
309
 
@@ -374,6 +383,9 @@ const toNodePoolUpdateBody = (
374
383
  if (nc.confidentialNodes && !deepEqual(oc.confidentialNodes, nc.confidentialNodes)) {
375
384
  body.confidentialNodes = nc.confidentialNodes;
376
385
  }
386
+ if (nc.gcfsConfig && !deepEqual(oc.gcfsConfig, nc.gcfsConfig)) {
387
+ body.gcfsConfig = nc.gcfsConfig;
388
+ }
377
389
  if (
378
390
  nc.workloadMetadataConfig &&
379
391
  !deepEqual(oc.workloadMetadataConfig, nc.workloadMetadataConfig)
@@ -321,9 +321,28 @@ export const ServiceAccountProvider = () =>
321
321
  stables: ["accountId", "projectId", "uniqueId", "name", "email"],
322
322
  diff: Effect.fn(function* ({ id, news, olds = {}, output }) {
323
323
  if (!isResolved(news)) return undefined;
324
+ // Replace ONLY when a prior identity is actually known AND differs.
325
+ // A partial/adopted state can lack the persisted accountId/projectId
326
+ // (an adoption that never persisted full Attributes leaves
327
+ // output.projectId === "" / undefined). Treating that "unknown" as a
328
+ // replace spuriously deletes+recreates a live, correctly-named GSA
329
+ // (and churns its uniqueId → breaks Workload-Identity bindings).
330
+ // When the prior identity is absent, fall through to reconcile, which
331
+ // observes the live resource and re-persists its Attributes. The
332
+ // truthiness guard covers both `undefined` and the `""` toAttributes
333
+ // default; a genuine accountId/project change still has a non-empty
334
+ // prior value to compare against, so real replacements are unaffected.
335
+ // `||` (not `??`): toAttributes persists `accountId`/`projectId` as
336
+ // `""` when absent, and an empty string must fall through to `olds`
337
+ // (the previous Props) rather than mask it — otherwise a genuine
338
+ // identity change with an empty `output` but a populated `olds` would
339
+ // skip the replace and adopt/create at the new identity without
340
+ // deleting the old GSA.
341
+ const priorAccount = output?.accountId || olds.accountId;
342
+ const priorProject = output?.projectId || olds.project;
324
343
  if (
325
- news.accountId !== (output?.accountId ?? olds.accountId) ||
326
- news.project !== (output?.projectId ?? olds.project)
344
+ (priorAccount && news.accountId !== priorAccount) ||
345
+ (priorProject && news.project !== priorProject)
327
346
  ) {
328
347
  return { action: "replace" } as const;
329
348
  }