@microagi/alchemy-gcp 0.2.1 → 0.3.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 (47) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +54 -2
  3. package/lib/Providers.d.ts.map +1 -1
  4. package/lib/Providers.js +5 -1
  5. package/lib/Providers.js.map +1 -1
  6. package/lib/Run/IamMember.d.ts +70 -0
  7. package/lib/Run/IamMember.d.ts.map +1 -0
  8. package/lib/Run/IamMember.js +65 -0
  9. package/lib/Run/IamMember.js.map +1 -0
  10. package/lib/Run/IamSync.d.ts +80 -0
  11. package/lib/Run/IamSync.d.ts.map +1 -0
  12. package/lib/Run/IamSync.js +73 -0
  13. package/lib/Run/IamSync.js.map +1 -0
  14. package/lib/Run/Job.d.ts +167 -0
  15. package/lib/Run/Job.d.ts.map +1 -0
  16. package/lib/Run/Job.js +199 -0
  17. package/lib/Run/Job.js.map +1 -0
  18. package/lib/Run/Operations.d.ts +47 -0
  19. package/lib/Run/Operations.d.ts.map +1 -0
  20. package/lib/Run/Operations.js +46 -0
  21. package/lib/Run/Operations.js.map +1 -0
  22. package/lib/Run/Service.d.ts +193 -0
  23. package/lib/Run/Service.d.ts.map +1 -0
  24. package/lib/Run/Service.js +247 -0
  25. package/lib/Run/Service.js.map +1 -0
  26. package/lib/Run/Validation.d.ts +54 -0
  27. package/lib/Run/Validation.d.ts.map +1 -0
  28. package/lib/Run/Validation.js +129 -0
  29. package/lib/Run/Validation.js.map +1 -0
  30. package/lib/Run/index.d.ts +51 -0
  31. package/lib/Run/index.d.ts.map +1 -0
  32. package/lib/Run/index.js +4 -0
  33. package/lib/Run/index.js.map +1 -0
  34. package/lib/index.d.ts +1 -0
  35. package/lib/index.d.ts.map +1 -1
  36. package/lib/index.js +1 -0
  37. package/lib/index.js.map +1 -1
  38. package/package.json +5 -4
  39. package/src/Providers.ts +6 -0
  40. package/src/Run/IamMember.ts +79 -0
  41. package/src/Run/IamSync.ts +134 -0
  42. package/src/Run/Job.ts +453 -0
  43. package/src/Run/Operations.ts +77 -0
  44. package/src/Run/Service.ts +541 -0
  45. package/src/Run/Validation.ts +153 -0
  46. package/src/Run/index.ts +52 -0
  47. package/src/index.ts +1 -0
@@ -0,0 +1,247 @@
1
+ import * as run from "@distilled.cloud/gcp/run-v2";
2
+ import { Resource } from "alchemy";
3
+ import { Unowned } from "alchemy/AdoptPolicy";
4
+ import { deepEqual, isResolved, somePropsAreDifferent } from "alchemy/Diff";
5
+ import { createPhysicalName } from "alchemy/PhysicalName";
6
+ import * as Provider from "alchemy/Provider";
7
+ import { diffTags } from "alchemy/Tags";
8
+ import * as Duration from "effect/Duration";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Schedule from "effect/Schedule";
11
+ import { gcpInternalLabels, hasAlchemyLabels } from "../Tags.js";
12
+ import { makeSyncIam } from "./IamSync.js";
13
+ import { makeAwaitOperation } from "./Operations.js";
14
+ import { reshapeBadRequest, validateAnnotations, validateContainers, validateLabels, validateRunName, } from "./Validation.js";
15
+ export const Service = Resource("GCP.Service");
16
+ const fqName = (project, location, name) => `projects/${project}/locations/${location}/services/${name}`;
17
+ const toServiceBody = (news, desiredLabels) => ({
18
+ ...(news.description !== undefined ? { description: news.description } : {}),
19
+ labels: desiredLabels,
20
+ ...(news.annotations ? { annotations: news.annotations } : {}),
21
+ ...(news.launchStage ? { launchStage: news.launchStage } : {}),
22
+ ...(news.ingress ? { ingress: news.ingress } : {}),
23
+ ...(news.invokerIamDisabled !== undefined
24
+ ? { invokerIamDisabled: news.invokerIamDisabled }
25
+ : {}),
26
+ ...(news.defaultUriDisabled !== undefined
27
+ ? { defaultUriDisabled: news.defaultUriDisabled }
28
+ : {}),
29
+ ...(news.scaling ? { scaling: news.scaling } : {}),
30
+ ...(news.traffic ? { traffic: news.traffic } : {}),
31
+ ...(news.customAudiences ? { customAudiences: news.customAudiences } : {}),
32
+ ...(news.binaryAuthorization
33
+ ? { binaryAuthorization: news.binaryAuthorization }
34
+ : {}),
35
+ template: news.template,
36
+ });
37
+ const toAttributes = (s, parent) => ({
38
+ name: parent.name,
39
+ uid: s.uid ?? "",
40
+ resourceName: s.name ?? fqName(parent.project, parent.location, parent.name),
41
+ project: parent.project,
42
+ location: parent.location,
43
+ uri: s.uri ?? "",
44
+ urls: s.urls ?? [],
45
+ generation: s.generation,
46
+ observedGeneration: s.observedGeneration,
47
+ latestReadyRevision: s.latestReadyRevision,
48
+ latestCreatedRevision: s.latestCreatedRevision,
49
+ terminalCondition: s.terminalCondition,
50
+ trafficStatuses: s.trafficStatuses ?? [],
51
+ etag: s.etag,
52
+ labels: { ...(s.labels ?? {}) },
53
+ createTime: s.createTime,
54
+ updateTime: s.updateTime,
55
+ });
56
+ export const ServiceProvider = () => Provider.effect(Service, Effect.gen(function* () {
57
+ const getService = yield* run.getProjectsLocationsServices;
58
+ const createService = yield* run.createProjectsLocationsServices;
59
+ const patchService = yield* run.patchProjectsLocationsServices;
60
+ const deleteService = yield* run.deleteProjectsLocationsServices;
61
+ const getOperation = yield* run.getProjectsLocationsOperations;
62
+ const getIamPolicy = yield* run.getIamPolicyProjectsLocationsServices;
63
+ const setIamPolicy = yield* run.setIamPolicyProjectsLocationsServices;
64
+ const awaitOperation = makeAwaitOperation(getOperation);
65
+ const syncIam = makeSyncIam({ getIamPolicy, setIamPolicy });
66
+ const observe = (project, location, name) => getService({ name: fqName(project, location, name) }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)), Effect.catchTag("Forbidden", () => Effect.succeed(undefined)));
67
+ const syncMutable = Effect.fn(function* (args) {
68
+ const desiredBody = toServiceBody(args.news, args.desiredLabels);
69
+ const updateMaskFields = [];
70
+ if ((args.observed.description ?? undefined) !== args.news.description) {
71
+ updateMaskFields.push("description");
72
+ }
73
+ const labelDiff = diffTags({ ...(args.observed.labels ?? {}) }, args.desiredLabels);
74
+ if (labelDiff.removed.length > 0 || labelDiff.upsert.length > 0) {
75
+ updateMaskFields.push("labels");
76
+ }
77
+ if (!deepEqual(args.observed.annotations ?? {}, args.news.annotations ?? {})) {
78
+ updateMaskFields.push("annotations");
79
+ }
80
+ if (args.news.launchStage !== undefined &&
81
+ args.observed.launchStage !== args.news.launchStage) {
82
+ updateMaskFields.push("launchStage");
83
+ }
84
+ if (args.news.ingress !== undefined &&
85
+ args.observed.ingress !== args.news.ingress) {
86
+ updateMaskFields.push("ingress");
87
+ }
88
+ if (args.news.invokerIamDisabled !== undefined &&
89
+ (args.observed.invokerIamDisabled ?? false) !==
90
+ args.news.invokerIamDisabled) {
91
+ updateMaskFields.push("invokerIamDisabled");
92
+ }
93
+ if (args.news.defaultUriDisabled !== undefined &&
94
+ (args.observed.defaultUriDisabled ?? false) !==
95
+ args.news.defaultUriDisabled) {
96
+ updateMaskFields.push("defaultUriDisabled");
97
+ }
98
+ if (args.news.scaling && !deepEqual(args.observed.scaling, args.news.scaling)) {
99
+ updateMaskFields.push("scaling");
100
+ }
101
+ if (args.news.traffic && !deepEqual(args.observed.traffic ?? [], args.news.traffic)) {
102
+ updateMaskFields.push("traffic");
103
+ }
104
+ if (args.news.customAudiences &&
105
+ !deepEqual(args.observed.customAudiences ?? [], args.news.customAudiences)) {
106
+ updateMaskFields.push("customAudiences");
107
+ }
108
+ if (args.news.binaryAuthorization &&
109
+ !deepEqual(args.observed.binaryAuthorization, args.news.binaryAuthorization)) {
110
+ updateMaskFields.push("binaryAuthorization");
111
+ }
112
+ // The Revision template is a deeply nested struct; diffing field
113
+ // by field would be miles of code with no payoff. Deep-equal at
114
+ // the top — patch sends the whole desired template if anything
115
+ // inside differs. Cloud Run spawns a new Revision on patch only
116
+ // when the template's content (modulo server-injected defaults)
117
+ // actually changes, so resending an equivalent template after a
118
+ // no-op reconcile is cheap.
119
+ if (!deepEqual(args.observed.template, args.news.template)) {
120
+ updateMaskFields.push("template");
121
+ }
122
+ if (updateMaskFields.length === 0)
123
+ return;
124
+ // Pass the observed etag through the body so a concurrent edit
125
+ // racing us surfaces as Conflict instead of silently
126
+ // overwriting. Cloud Run reads etag from the body (it's a
127
+ // field on GoogleCloudRunV2Service, not a request-level param).
128
+ const bodyWithEtag = {
129
+ ...desiredBody,
130
+ ...(args.observed.etag ? { etag: args.observed.etag } : {}),
131
+ };
132
+ const op = yield* patchService({
133
+ name: args.name,
134
+ updateMask: updateMaskFields.join(","),
135
+ body: bodyWithEtag,
136
+ }).pipe(Effect.catchTag("BadRequest", reshapeBadRequest("Service", "patch")));
137
+ if (op.name)
138
+ yield* awaitOperation(op.name, args.session);
139
+ });
140
+ return {
141
+ stables: ["name", "uid", "resourceName", "project", "location"],
142
+ diff: Effect.fn(function* ({ news, olds = {} }) {
143
+ if (!isResolved(news))
144
+ return undefined;
145
+ if (somePropsAreDifferent(olds, news, [
146
+ "project",
147
+ "location",
148
+ "name",
149
+ ])) {
150
+ return { action: "replace" };
151
+ }
152
+ return undefined;
153
+ }),
154
+ reconcile: Effect.fn(function* ({ id, news, session, bindings }) {
155
+ const internalLabels = yield* gcpInternalLabels(id);
156
+ // Cloud Run service IDs must be <50 chars (server hard cap).
157
+ const desiredName = news.name ??
158
+ (yield* createPhysicalName({ id, maxLength: 49 })).toLowerCase();
159
+ // Plan-time validation: surface common footguns as typed
160
+ // ConfigErrors before any state mutation. The server would
161
+ // reject these too, but a 30 s wait + half-built state is a
162
+ // worse experience than failing immediately.
163
+ yield* validateRunName("Service", desiredName);
164
+ yield* validateLabels(news.labels);
165
+ yield* validateAnnotations(news.annotations);
166
+ yield* validateContainers("Service", news.template.containers);
167
+ const parent = `projects/${news.project}/locations/${news.location}`;
168
+ const name = fqName(news.project, news.location, desiredName);
169
+ const desiredLabels = {
170
+ ...(news.labels ?? {}),
171
+ ...internalLabels,
172
+ };
173
+ // 1. Observe — collapse 403 to "missing" alongside 404 (GCP
174
+ // surfaces 403 for resources/projects we can't see).
175
+ let observed = yield* observe(news.project, news.location, desiredName);
176
+ // 2. Ensure — same API-enable race as Cluster: a create can
177
+ // fire faster than ApiEnable's effect propagates and the
178
+ // server returns 403 with "If you enabled this API
179
+ // recently, wait a few minutes …". Retry the create call
180
+ // on that specific 403 for ~5 min; other Forbiddens
181
+ // propagate.
182
+ if (!observed) {
183
+ const op = yield* createService({
184
+ parent,
185
+ serviceId: desiredName,
186
+ body: toServiceBody(news, desiredLabels),
187
+ }).pipe(Effect.retry({
188
+ while: (e) => e?._tag === "Forbidden" &&
189
+ /enabled this API recently|has not been used/i.test(e.message ?? ""),
190
+ schedule: Schedule.spaced(Duration.seconds(15)).pipe(Schedule.both(Schedule.recurs(20)), Schedule.tapOutput(() => session.note("Waiting for Cloud Run API enablement to propagate…"))),
191
+ }), Effect.catchTag("Conflict", () => Effect.succeed(undefined)), Effect.catchTag("BadRequest", reshapeBadRequest("Service", "create")));
192
+ if (op?.name)
193
+ yield* awaitOperation(op.name, session);
194
+ observed = yield* getService({ name });
195
+ }
196
+ // 3. Sync — single patch with an updateMask of changed
197
+ // top-level fields. Template is diff'd shallowly (deep
198
+ // equality on the whole struct).
199
+ yield* syncMutable({
200
+ name,
201
+ observed,
202
+ news,
203
+ desiredLabels,
204
+ session,
205
+ });
206
+ // 4. Apply IAM bindings AFTER the service exists. Single
207
+ // setIamPolicy call covers all bindings for this service;
208
+ // foreign bindings are preserved verbatim. Etag round-trip
209
+ // handles concurrent edits.
210
+ yield* syncIam({ resource: name, bindings });
211
+ const final = yield* getService({ name });
212
+ return toAttributes(final, {
213
+ project: news.project,
214
+ location: news.location,
215
+ name: desiredName,
216
+ });
217
+ }),
218
+ delete: Effect.fn(function* ({ output, session }) {
219
+ const name = fqName(output.project, output.location, output.name);
220
+ // Pass the etag we stored at last reconcile so a concurrent
221
+ // edit racing this delete surfaces as Conflict. If we never
222
+ // captured one (legacy state, adoption path), omit the
223
+ // parameter — Cloud Run treats it as "don't check".
224
+ yield* deleteService({
225
+ name,
226
+ ...(output.etag ? { etag: output.etag } : {}),
227
+ }).pipe(Effect.flatMap((op) => op.name ? awaitOperation(op.name, session) : Effect.succeed(op)), Effect.catchTag("NotFound", () => Effect.void));
228
+ }),
229
+ read: Effect.fn(function* ({ id, output, olds }) {
230
+ const project = output?.project ?? olds?.project;
231
+ const location = output?.location ?? olds?.location;
232
+ if (!project || !location)
233
+ return undefined;
234
+ const name = output?.name ??
235
+ olds?.name ??
236
+ (yield* createPhysicalName({ id, maxLength: 49 })).toLowerCase();
237
+ const observed = yield* observe(project, location, name);
238
+ if (!observed)
239
+ return undefined;
240
+ const attrs = toAttributes(observed, { project, location, name });
241
+ return (yield* hasAlchemyLabels(id, observed.labels))
242
+ ? attrs
243
+ : Unowned(attrs);
244
+ }),
245
+ };
246
+ }));
247
+ //# sourceMappingURL=Service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Service.js","sourceRoot":"","sources":["../../src/Run/Service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { ConfigError } from "@distilled.cloud/gcp";
2
+ import * as Effect from "effect/Effect";
3
+ /**
4
+ * Validate a Cloud Run resource name (Service or Job). Returns a
5
+ * `ConfigError` Effect on the failure channel if the name is invalid,
6
+ * otherwise succeeds.
7
+ *
8
+ * The name comes from `props.name` (when the user supplied one) or
9
+ * `createPhysicalName({ id, maxLength: 49 })` (when defaulted). The
10
+ * defaulted path is already capped at 49; this guard mostly protects
11
+ * the user-supplied path against a server-side reject after a long
12
+ * deploy.
13
+ */
14
+ export declare const validateRunName: (kind: "Service" | "Job", name: string) => Effect.Effect<void, ConfigError>;
15
+ /**
16
+ * Reject user labels in any of the Cloud Run reserved namespaces — the
17
+ * server's 400 message ("Labels with run.googleapis.com namespace are
18
+ * not allowed") is clear enough, but failing at plan time means the
19
+ * stack never starts a deploy that's guaranteed to fail.
20
+ *
21
+ * Alchemy internals are merged *on top* of user labels and are NOT
22
+ * subject to this check — they use the un-namespaced `alchemy_*` keys.
23
+ */
24
+ export declare const validateLabels: (labels: Record<string, string> | undefined) => Effect.Effect<void, ConfigError>;
25
+ /** Same check, applied to annotations (server has the same reservation). */
26
+ export declare const validateAnnotations: (annotations: Record<string, string> | undefined) => Effect.Effect<void, ConfigError>;
27
+ /**
28
+ * Cloud Run requires every Service template and Job task template to
29
+ * declare at least one container. A `containers: []` body sails through
30
+ * client-side type checks but the server rejects with a generic 400.
31
+ */
32
+ export declare const validateContainers: (kind: "Service" | "Job", containers: ReadonlyArray<unknown> | undefined) => Effect.Effect<void, ConfigError>;
33
+ /**
34
+ * Reshape a `BadRequest` from a Cloud Run create or patch into a
35
+ * `ConfigError` with a remediation hint for the top failure modes:
36
+ *
37
+ * 1. **Billing not enabled** — the project's billing-account attach
38
+ * was missing or detached. Common after a fresh project + ApiEnable
39
+ * before billing reconciles.
40
+ * 2. **Image not in an approved registry** — Cloud Run defaults reject
41
+ * Docker Hub and most non-Google registries when Binary Authorization
42
+ * is on. Surface the registry-allow-list pointer.
43
+ * 3. **Reserved label/annotation namespace** — we catch user-supplied
44
+ * cases in `validateLabels`, but server-side checks also fire for
45
+ * fields nested inside `template` (we don't recurse) so leave this
46
+ * as a passthrough hint.
47
+ *
48
+ * Anything else propagates verbatim — the underlying GCP message is
49
+ * usually clear ("invalid image reference", "memory limit too low").
50
+ */
51
+ export declare const reshapeBadRequest: (kind: "Service" | "Job", op: "create" | "patch") => (e: {
52
+ message?: string;
53
+ }) => Effect.Effect<never, ConfigError>;
54
+ //# sourceMappingURL=Validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Validation.d.ts","sourceRoot":"","sources":["../../src/Run/Validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AA0CxC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,GAAG,KAAK,EACvB,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAOjC,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,KACzC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAajC,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,GAC9B,aAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,KAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAgC,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,SAAS,GAAG,KAAK,EACvB,YAAY,aAAa,CAAC,OAAO,CAAC,GAAG,SAAS,KAC7C,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAOjC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,GAC3B,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,QAAQ,GAAG,OAAO,MAC/C,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAkB1D,CAAC"}
@@ -0,0 +1,129 @@
1
+ import { ConfigError } from "@distilled.cloud/gcp";
2
+ import * as Effect from "effect/Effect";
3
+ /**
4
+ * Plan-time validation for Cloud Run resource props. Failing fast with
5
+ * a typed `ConfigError` is much friendlier than letting the user wait
6
+ * 30 s for the server to reject the request — and unlike server-side
7
+ * errors, these surface before any state mutation happens.
8
+ *
9
+ * Used by both `Service` and `Job` since the constraints are shared:
10
+ * resource naming, label namespacing, and the "at least one container"
11
+ * rule on the template.
12
+ */
13
+ /**
14
+ * Cloud Run resource names must match the
15
+ * [RFC 1123](https://datatracker.ietf.org/doc/html/rfc1123) label
16
+ * subset:
17
+ * - lowercase letters, digits, and hyphens
18
+ * - begin with a letter, not end with a hyphen
19
+ * - <50 chars (server hard cap, distinct from the wider 63-char label
20
+ * limit other GCP resources use)
21
+ *
22
+ * @internal
23
+ */
24
+ const RUN_NAME_RE = /^[a-z]([-a-z0-9]{0,47}[a-z0-9])?$/;
25
+ /**
26
+ * Cloud Run rejects labels and annotations with keys in any of these
27
+ * namespaces. Listed in `vendor/distilled/packages/gcp/src/services/run-v2.ts`
28
+ * doc comments next to every `labels` / `annotations` field. Failing
29
+ * fast here saves a 400 round-trip from the server.
30
+ */
31
+ const RESERVED_LABEL_NAMESPACES = [
32
+ "run.googleapis.com/",
33
+ "cloud.googleapis.com/",
34
+ "serving.knative.dev/",
35
+ "autoscaling.knative.dev/",
36
+ ];
37
+ const reservedNamespaceFor = (key) => RESERVED_LABEL_NAMESPACES.find((ns) => key.startsWith(ns));
38
+ /**
39
+ * Validate a Cloud Run resource name (Service or Job). Returns a
40
+ * `ConfigError` Effect on the failure channel if the name is invalid,
41
+ * otherwise succeeds.
42
+ *
43
+ * The name comes from `props.name` (when the user supplied one) or
44
+ * `createPhysicalName({ id, maxLength: 49 })` (when defaulted). The
45
+ * defaulted path is already capped at 49; this guard mostly protects
46
+ * the user-supplied path against a server-side reject after a long
47
+ * deploy.
48
+ */
49
+ export const validateRunName = (kind, name) => {
50
+ if (RUN_NAME_RE.test(name) && name.length < 50)
51
+ return Effect.void;
52
+ return Effect.fail(new ConfigError({
53
+ message: `Cloud Run ${kind} name ${JSON.stringify(name)} is invalid: must match /^[a-z]([-a-z0-9]{0,47}[a-z0-9])?$/ (lowercase letters/digits/hyphens, starts with letter, no trailing hyphen, <50 chars).`,
54
+ }));
55
+ };
56
+ /**
57
+ * Reject user labels in any of the Cloud Run reserved namespaces — the
58
+ * server's 400 message ("Labels with run.googleapis.com namespace are
59
+ * not allowed") is clear enough, but failing at plan time means the
60
+ * stack never starts a deploy that's guaranteed to fail.
61
+ *
62
+ * Alchemy internals are merged *on top* of user labels and are NOT
63
+ * subject to this check — they use the un-namespaced `alchemy_*` keys.
64
+ */
65
+ export const validateLabels = (labels) => {
66
+ if (!labels)
67
+ return Effect.void;
68
+ for (const key of Object.keys(labels)) {
69
+ const ns = reservedNamespaceFor(key);
70
+ if (ns) {
71
+ return Effect.fail(new ConfigError({
72
+ message: `Label key ${JSON.stringify(key)} uses reserved namespace ${JSON.stringify(ns)} — Cloud Run will reject this on create/patch. Strip the prefix or pick a different key.`,
73
+ }));
74
+ }
75
+ }
76
+ return Effect.void;
77
+ };
78
+ /** Same check, applied to annotations (server has the same reservation). */
79
+ export const validateAnnotations = (annotations) => validateLabels(annotations);
80
+ /**
81
+ * Cloud Run requires every Service template and Job task template to
82
+ * declare at least one container. A `containers: []` body sails through
83
+ * client-side type checks but the server rejects with a generic 400.
84
+ */
85
+ export const validateContainers = (kind, containers) => {
86
+ if (containers && containers.length > 0)
87
+ return Effect.void;
88
+ return Effect.fail(new ConfigError({
89
+ message: `Cloud Run ${kind} template must declare at least one container. Did you forget \`template.containers: [{ image: "…" }]\`?`,
90
+ }));
91
+ };
92
+ /**
93
+ * Reshape a `BadRequest` from a Cloud Run create or patch into a
94
+ * `ConfigError` with a remediation hint for the top failure modes:
95
+ *
96
+ * 1. **Billing not enabled** — the project's billing-account attach
97
+ * was missing or detached. Common after a fresh project + ApiEnable
98
+ * before billing reconciles.
99
+ * 2. **Image not in an approved registry** — Cloud Run defaults reject
100
+ * Docker Hub and most non-Google registries when Binary Authorization
101
+ * is on. Surface the registry-allow-list pointer.
102
+ * 3. **Reserved label/annotation namespace** — we catch user-supplied
103
+ * cases in `validateLabels`, but server-side checks also fire for
104
+ * fields nested inside `template` (we don't recurse) so leave this
105
+ * as a passthrough hint.
106
+ *
107
+ * Anything else propagates verbatim — the underlying GCP message is
108
+ * usually clear ("invalid image reference", "memory limit too low").
109
+ */
110
+ export const reshapeBadRequest = (kind, op) => (e) => {
111
+ const underlying = e.message ?? `unknown 400 from ${kind} ${op}`;
112
+ let hint = "";
113
+ if (/billing/i.test(underlying)) {
114
+ hint =
115
+ " Cloud Run requires billing to be enabled on the project. Attach a billing account (see `GCP.Project({ billingAccount })`) and retry.";
116
+ }
117
+ else if (/registry|image.*not.*allowed|disallowed.*image/i.test(underlying)) {
118
+ hint =
119
+ " Cloud Run rejects images from non-Google registries when Binary Authorization is on. Push to Artifact Registry (`*-docker.pkg.dev`) or relax the binary-auth policy.";
120
+ }
121
+ else if (/run\.googleapis\.com|knative\.dev|cloud\.googleapis\.com/i.test(underlying)) {
122
+ hint =
123
+ " A reserved label/annotation namespace is in use. Cloud Run forbids `run.googleapis.com/*`, `cloud.googleapis.com/*`, `serving.knative.dev/*`, and `autoscaling.knative.dev/*` on user fields.";
124
+ }
125
+ return Effect.fail(new ConfigError({
126
+ message: `Cloud Run ${kind} ${op} rejected: ${underlying}.${hint}`,
127
+ }));
128
+ };
129
+ //# sourceMappingURL=Validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Validation.js","sourceRoot":"","sources":["../../src/Run/Validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;;;;;;GASG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,GAAG,mCAAmC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,yBAAyB,GAAG;IAChC,qBAAqB;IACrB,uBAAuB;IACvB,sBAAsB;IACtB,0BAA0B;CAC3B,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAAsB,EAAE,CAC/D,yBAAyB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;AAE7D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAuB,EACvB,IAAY,EACsB,EAAE;IACpC,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IACnE,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;QACd,OAAO,EAAE,aAAa,IAAI,SAAS,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,oJAAoJ;KAC5M,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,MAA0C,EACR,EAAE;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,EAAE,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,EAAE,EAAE,CAAC;YACP,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;gBACd,OAAO,EAAE,aAAa,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,0FAA0F;aAClL,CAAC,CACH,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC,CAAC;AAEF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,WAA+C,EACb,EAAE,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;AAEnE;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,IAAuB,EACvB,UAA8C,EACZ,EAAE;IACpC,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IAC5D,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;QACd,OAAO,EAAE,aAAa,IAAI,0GAA0G;KACrI,CAAC,CACH,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAC5B,CAAC,IAAuB,EAAE,EAAsB,EAAE,EAAE,CACpD,CAAC,CAAuB,EAAqC,EAAE;IAC7D,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,IAAI,oBAAoB,IAAI,IAAI,EAAE,EAAE,CAAC;IACjE,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,IAAI;YACF,uIAAuI,CAAC;IAC5I,CAAC;SAAM,IAAI,iDAAiD,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9E,IAAI;YACF,uKAAuK,CAAC;IAC5K,CAAC;SAAM,IAAI,2DAA2D,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxF,IAAI;YACF,gMAAgM,CAAC;IACrM,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,WAAW,CAAC;QACd,OAAO,EAAE,aAAa,IAAI,IAAI,EAAE,cAAc,UAAU,IAAI,IAAI,EAAE;KACnE,CAAC,CACH,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type * as run from "@distilled.cloud/gcp/run-v2";
2
+ export { Job, JobProvider } from "./Job.ts";
3
+ export type { JobAttributes, JobProps } from "./Job.ts";
4
+ export { jobIamMember, serviceIamMember } from "./IamMember.ts";
5
+ export type { RunIamBinding, RunIamBindingContract } from "./IamSync.ts";
6
+ export { Service, ServiceProvider } from "./Service.ts";
7
+ export type { ServiceAttributes, ServiceProps } from "./Service.ts";
8
+ /**
9
+ * Clean aliases for the most commonly-used Cloud Run v2 SDK types,
10
+ * re-exported from `@distilled.cloud/gcp/run-v2` so consumers don't
11
+ * need to import from the underlying SDK directly. The aliases are
12
+ * type-only — they share identity with the distilled types, so values
13
+ * are interchangeable.
14
+ *
15
+ * The `GoogleCloudRunV2*` prefix on the SDK types is generated from
16
+ * the GCP Discovery Document and is awkward to consume; these aliases
17
+ * mirror the casing the GCP REST docs use ("RevisionTemplate",
18
+ * "Container", "TrafficTarget"), which is what most users will
19
+ * already have in their heads.
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * import * as GCP from "@microagi/alchemy-gcp";
24
+ *
25
+ * const template: GCP.RevisionTemplate = {
26
+ * containers: [{ image: "..." }],
27
+ * };
28
+ * ```
29
+ */
30
+ export type RevisionTemplate = run.GoogleCloudRunV2RevisionTemplate;
31
+ export type ExecutionTemplate = run.GoogleCloudRunV2ExecutionTemplate;
32
+ export type TaskTemplate = run.GoogleCloudRunV2TaskTemplate;
33
+ export type Container = run.GoogleCloudRunV2Container;
34
+ export type ContainerPort = run.GoogleCloudRunV2ContainerPort;
35
+ export type EnvVar = run.GoogleCloudRunV2EnvVar;
36
+ export type EnvVarSource = run.GoogleCloudRunV2EnvVarSource;
37
+ export type ResourceRequirements = run.GoogleCloudRunV2ResourceRequirements;
38
+ export type Probe = run.GoogleCloudRunV2Probe;
39
+ export type VpcAccess = run.GoogleCloudRunV2VpcAccess;
40
+ export type NetworkInterface = run.GoogleCloudRunV2NetworkInterface;
41
+ export type NodeSelector = run.GoogleCloudRunV2NodeSelector;
42
+ export type TrafficTarget = run.GoogleCloudRunV2TrafficTarget;
43
+ export type TrafficTargetStatus = run.GoogleCloudRunV2TrafficTargetStatus;
44
+ export type ServiceScaling = run.GoogleCloudRunV2ServiceScaling;
45
+ export type BinaryAuthorization = run.GoogleCloudRunV2BinaryAuthorization;
46
+ export type Volume = run.GoogleCloudRunV2Volume;
47
+ export type VolumeMount = run.GoogleCloudRunV2VolumeMount;
48
+ export type Condition = run.GoogleCloudRunV2Condition;
49
+ export type ExecutionReference = run.GoogleCloudRunV2ExecutionReference;
50
+ export type SecretKeySelector = run.GoogleCloudRunV2SecretKeySelector;
51
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Run/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,6BAA6B,CAAC;AAExD,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,gCAAgC,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,iCAAiC,CAAC;AACtE,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,4BAA4B,CAAC;AAC5D,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,yBAAyB,CAAC;AACtD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,6BAA6B,CAAC;AAC9D,MAAM,MAAM,MAAM,GAAG,GAAG,CAAC,sBAAsB,CAAC;AAChD,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,4BAA4B,CAAC;AAC5D,MAAM,MAAM,oBAAoB,GAAG,GAAG,CAAC,oCAAoC,CAAC;AAC5E,MAAM,MAAM,KAAK,GAAG,GAAG,CAAC,qBAAqB,CAAC;AAC9C,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,yBAAyB,CAAC;AACtD,MAAM,MAAM,gBAAgB,GAAG,GAAG,CAAC,gCAAgC,CAAC;AACpE,MAAM,MAAM,YAAY,GAAG,GAAG,CAAC,4BAA4B,CAAC;AAC5D,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,6BAA6B,CAAC;AAC9D,MAAM,MAAM,mBAAmB,GAAG,GAAG,CAAC,mCAAmC,CAAC;AAC1E,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,8BAA8B,CAAC;AAChE,MAAM,MAAM,mBAAmB,GAAG,GAAG,CAAC,mCAAmC,CAAC;AAC1E,MAAM,MAAM,MAAM,GAAG,GAAG,CAAC,sBAAsB,CAAC;AAChD,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,2BAA2B,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,yBAAyB,CAAC;AACtD,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,kCAAkC,CAAC;AACxE,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,iCAAiC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Job, JobProvider } from "./Job.js";
2
+ export { jobIamMember, serviceIamMember } from "./IamMember.js";
3
+ export { Service, ServiceProvider } from "./Service.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Run/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
package/lib/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./CloudResourceManager/index.ts";
5
5
  export * from "./Compute/index.ts";
6
6
  export * from "./Container/index.ts";
7
7
  export * from "./ManagedLustre/index.ts";
8
+ export * from "./Run/index.ts";
8
9
  export * from "./ServiceNetworking/index.ts";
9
10
  export * from "./ServiceUsage/index.ts";
10
11
  export * from "./Providers.ts";
@@ -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,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,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,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC"}
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ export * from "./CloudResourceManager/index.js";
4
4
  export * from "./Compute/index.js";
5
5
  export * from "./Container/index.js";
6
6
  export * from "./ManagedLustre/index.js";
7
+ export * from "./Run/index.js";
7
8
  export * from "./ServiceNetworking/index.js";
8
9
  export * from "./ServiceUsage/index.js";
9
10
  export * from "./Providers.js";
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,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,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,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@microagi/alchemy-gcp",
3
- "version": "0.2.1",
4
- "description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, Cluster, NodePool, Compute, ManagedLustre, ServiceUsage, ServiceNetworking, with ADC-backed credentials and target-side IAM bindings.",
3
+ "version": "0.3.0",
4
+ "description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, Cluster, NodePool, Compute, ManagedLustre, ServiceUsage, ServiceNetworking, Cloud Run (Service + Job + IAM), with ADC-backed credentials and target-side IAM bindings.",
5
5
  "keywords": [
6
6
  "alchemy",
7
7
  "alchemy-effect",
@@ -31,6 +31,7 @@
31
31
  "files": [
32
32
  "lib",
33
33
  "src",
34
+ "CHANGELOG.md",
34
35
  "README.md",
35
36
  "LICENSE"
36
37
  ],
@@ -57,10 +58,10 @@
57
58
  "effect": ">=4.0.0-beta.60 || >=4.0.0"
58
59
  },
59
60
  "devDependencies": {
60
- "@distilled.cloud/gcp": "0.19.0",
61
+ "@distilled.cloud/gcp": "workspace:*",
61
62
  "@types/bun": "latest",
62
63
  "@types/node": "latest",
63
- "alchemy": "2.0.0-beta.34",
64
+ "alchemy": "workspace:*",
64
65
  "effect": ">=4.0.0-beta.60 || >=4.0.0",
65
66
  "typescript": "^6.0.3"
66
67
  },
package/src/Providers.ts CHANGED
@@ -17,6 +17,8 @@ import {
17
17
  ManagedLustreInstance,
18
18
  ManagedLustreInstanceProvider,
19
19
  } from "./ManagedLustre/Instance.ts";
20
+ import { Job, JobProvider } from "./Run/Job.ts";
21
+ import { Service, ServiceProvider } from "./Run/Service.ts";
20
22
  import {
21
23
  PsaConnection,
22
24
  PsaConnectionProvider,
@@ -57,6 +59,8 @@ export const providers = () =>
57
59
  SharedVpcServiceProject,
58
60
  PsaConnection,
59
61
  ManagedLustreInstance,
62
+ Service,
63
+ Job,
60
64
  ]),
61
65
  ).pipe(
62
66
  Layer.provide(
@@ -72,6 +76,8 @@ export const providers = () =>
72
76
  SharedVpcServiceProjectProvider(),
73
77
  PsaConnectionProvider(),
74
78
  ManagedLustreInstanceProvider(),
79
+ ServiceProvider(),
80
+ JobProvider(),
75
81
  ),
76
82
  ),
77
83
  Layer.provideMerge(fromAuthProvider()),