@microagi/alchemy-gcp 0.3.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +124 -0
- package/lib/ArtifactRegistry/Operations.d.ts +47 -0
- package/lib/ArtifactRegistry/Operations.d.ts.map +1 -0
- package/lib/ArtifactRegistry/Operations.js +47 -0
- package/lib/ArtifactRegistry/Operations.js.map +1 -0
- package/lib/ArtifactRegistry/Repository.d.ts +263 -0
- package/lib/ArtifactRegistry/Repository.d.ts.map +1 -0
- package/lib/ArtifactRegistry/Repository.js +256 -0
- package/lib/ArtifactRegistry/Repository.js.map +1 -0
- package/lib/ArtifactRegistry/Types.d.ts +32 -0
- package/lib/ArtifactRegistry/Types.d.ts.map +1 -0
- package/lib/ArtifactRegistry/Types.js +12 -0
- package/lib/ArtifactRegistry/Types.js.map +1 -0
- package/lib/ArtifactRegistry/Validation.d.ts +43 -0
- package/lib/ArtifactRegistry/Validation.d.ts.map +1 -0
- package/lib/ArtifactRegistry/Validation.js +95 -0
- package/lib/ArtifactRegistry/Validation.js.map +1 -0
- package/lib/ArtifactRegistry/index.d.ts +4 -0
- package/lib/ArtifactRegistry/index.d.ts.map +1 -0
- package/lib/ArtifactRegistry/index.js +2 -0
- package/lib/ArtifactRegistry/index.js.map +1 -0
- package/lib/Auth/Credentials.d.ts +2 -1
- package/lib/Auth/Credentials.d.ts.map +1 -1
- package/lib/Auth/Credentials.js +3 -2
- package/lib/Auth/Credentials.js.map +1 -1
- package/lib/CloudResourceManager/Project.d.ts +1 -1
- package/lib/Compute/GlobalAddress.d.ts +1 -1
- package/lib/Compute/Network.d.ts +1 -1
- package/lib/Compute/SharedVpcHost.d.ts +1 -1
- package/lib/Compute/SharedVpcServiceProject.d.ts +1 -1
- package/lib/Compute/Subnetwork.d.ts +1 -1
- package/lib/Container/Cluster.d.ts +1 -1
- package/lib/Container/NodePool.d.ts +1 -1
- package/lib/ManagedLustre/Instance.d.ts +1 -1
- package/lib/Providers.d.ts +1 -1
- package/lib/Providers.d.ts.map +1 -1
- package/lib/Providers.js +9 -1
- package/lib/Providers.js.map +1 -1
- package/lib/Run/Job.d.ts +1 -1
- package/lib/Run/Service.d.ts +1 -1
- package/lib/ServiceNetworking/PsaConnection.d.ts +1 -1
- package/lib/ServiceUsage/ApiEnable.d.ts +1 -1
- package/lib/Sqladmin/Database.d.ts +82 -0
- package/lib/Sqladmin/Database.d.ts.map +1 -0
- package/lib/Sqladmin/Database.js +87 -0
- package/lib/Sqladmin/Database.js.map +1 -0
- package/lib/Sqladmin/Instance.d.ts +234 -0
- package/lib/Sqladmin/Instance.d.ts.map +1 -0
- package/lib/Sqladmin/Instance.js +251 -0
- package/lib/Sqladmin/Instance.js.map +1 -0
- package/lib/Sqladmin/Operations.d.ts +41 -0
- package/lib/Sqladmin/Operations.d.ts.map +1 -0
- package/lib/Sqladmin/Operations.js +42 -0
- package/lib/Sqladmin/Operations.js.map +1 -0
- package/lib/Sqladmin/Types.d.ts +82 -0
- package/lib/Sqladmin/Types.d.ts.map +1 -0
- package/lib/Sqladmin/Types.js +28 -0
- package/lib/Sqladmin/Types.js.map +1 -0
- package/lib/Sqladmin/User.d.ts +110 -0
- package/lib/Sqladmin/User.d.ts.map +1 -0
- package/lib/Sqladmin/User.js +156 -0
- package/lib/Sqladmin/User.js.map +1 -0
- package/lib/Sqladmin/Validation.d.ts +60 -0
- package/lib/Sqladmin/Validation.d.ts.map +1 -0
- package/lib/Sqladmin/Validation.js +125 -0
- package/lib/Sqladmin/Validation.js.map +1 -0
- package/lib/Sqladmin/index.d.ts +9 -0
- package/lib/Sqladmin/index.d.ts.map +1 -0
- package/lib/Sqladmin/index.js +5 -0
- package/lib/Sqladmin/index.js.map +1 -0
- package/lib/Tags.d.ts +4 -4
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/ArtifactRegistry/Operations.ts +79 -0
- package/src/ArtifactRegistry/Repository.ts +628 -0
- package/src/ArtifactRegistry/Types.ts +49 -0
- package/src/ArtifactRegistry/Validation.ts +112 -0
- package/src/ArtifactRegistry/index.ts +10 -0
- package/src/Auth/Credentials.ts +3 -2
- package/src/Providers.ts +15 -0
- package/src/Sqladmin/Database.ts +209 -0
- package/src/Sqladmin/Instance.ts +620 -0
- package/src/Sqladmin/Operations.ts +73 -0
- package/src/Sqladmin/Types.ts +101 -0
- package/src/Sqladmin/User.ts +333 -0
- package/src/Sqladmin/Validation.ts +148 -0
- package/src/Sqladmin/index.ts +19 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import * as sql from "@distilled.cloud/gcp/sqladmin-v1";
|
|
2
|
+
import { Resource } from "alchemy";
|
|
3
|
+
import { isResolved, somePropsAreDifferent } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Redacted from "effect/Redacted";
|
|
7
|
+
import { makeAwaitOperation } from "./Operations.js";
|
|
8
|
+
import { isIamUserType } from "./Types.js";
|
|
9
|
+
import { reshapeBadRequest, validateUser } from "./Validation.js";
|
|
10
|
+
export const SqlUser = Resource("GCP.SqlUser");
|
|
11
|
+
const toAttributes = (u, parent) => ({
|
|
12
|
+
name: parent.name,
|
|
13
|
+
instance: parent.instance,
|
|
14
|
+
project: parent.project,
|
|
15
|
+
host: u.host ?? parent.host,
|
|
16
|
+
type: u.type ?? "BUILT_IN",
|
|
17
|
+
});
|
|
18
|
+
export const SqlUserProvider = () => Provider.effect(SqlUser, Effect.gen(function* () {
|
|
19
|
+
const getUsers = yield* sql.getUsers;
|
|
20
|
+
const listUsers = yield* sql.listUsers;
|
|
21
|
+
const insertUsers = yield* sql.insertUsers;
|
|
22
|
+
const updateUsers = yield* sql.updateUsers;
|
|
23
|
+
const deleteUsers = yield* sql.deleteUsers;
|
|
24
|
+
const getOperations = yield* sql.getOperations;
|
|
25
|
+
const awaitOperation = makeAwaitOperation(getOperations);
|
|
26
|
+
/**
|
|
27
|
+
* Observe a user. Cloud SQL's `getUsers` endpoint returns 404
|
|
28
|
+
* for IAM-typed users in some Postgres minor versions — those
|
|
29
|
+
* are listable but not directly addressable by `name`. Fall
|
|
30
|
+
* back to a list filter on 404 to be safe.
|
|
31
|
+
*/
|
|
32
|
+
const observe = (project, instance, name, host) => getUsers({
|
|
33
|
+
project,
|
|
34
|
+
instance,
|
|
35
|
+
name,
|
|
36
|
+
...(host ? { host } : {}),
|
|
37
|
+
}).pipe(Effect.catchTag("NotFound", () => listUsers({ project, instance }).pipe(Effect.map((res) => (res.items ?? []).find((u) => u.name === name && (host ? u.host === host : true))), Effect.catchTag("NotFound", () => Effect.succeed(undefined)), Effect.catchTag("Forbidden", () => Effect.succeed(undefined)))), Effect.catchTag("Forbidden", () => Effect.succeed(undefined)));
|
|
38
|
+
return {
|
|
39
|
+
stables: ["name", "instance", "project", "host", "type"],
|
|
40
|
+
diff: Effect.fn(function* ({ news, olds = {} }) {
|
|
41
|
+
if (!isResolved(news))
|
|
42
|
+
return undefined;
|
|
43
|
+
if (somePropsAreDifferent(olds, news, [
|
|
44
|
+
"project",
|
|
45
|
+
"instance",
|
|
46
|
+
"name",
|
|
47
|
+
"host",
|
|
48
|
+
"type",
|
|
49
|
+
])) {
|
|
50
|
+
return { action: "replace" };
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}),
|
|
54
|
+
reconcile: Effect.fn(function* ({ news, session }) {
|
|
55
|
+
const desiredType = news.type ?? "BUILT_IN";
|
|
56
|
+
const desiredHost = news.host ?? "%";
|
|
57
|
+
yield* validateUser({
|
|
58
|
+
name: news.name,
|
|
59
|
+
type: desiredType,
|
|
60
|
+
hasPassword: news.password !== undefined,
|
|
61
|
+
});
|
|
62
|
+
let observed = yield* observe(news.project, news.instance, news.name, news.host);
|
|
63
|
+
if (!observed) {
|
|
64
|
+
const body = {
|
|
65
|
+
name: news.name,
|
|
66
|
+
...(news.host !== undefined ? { host: news.host } : {}),
|
|
67
|
+
type: desiredType,
|
|
68
|
+
...(news.password
|
|
69
|
+
? { password: Redacted.value(news.password) }
|
|
70
|
+
: {}),
|
|
71
|
+
};
|
|
72
|
+
const op = yield* insertUsers({
|
|
73
|
+
project: news.project,
|
|
74
|
+
instance: news.instance,
|
|
75
|
+
body,
|
|
76
|
+
}).pipe(Effect.catchTag("Conflict", () => Effect.succeed(undefined)), Effect.catchTag("BadRequest", reshapeBadRequest("User", "insert")));
|
|
77
|
+
if (op?.name)
|
|
78
|
+
yield* awaitOperation(news.project, op.name, session);
|
|
79
|
+
observed = yield* observe(news.project, news.instance, news.name, news.host);
|
|
80
|
+
}
|
|
81
|
+
else if (
|
|
82
|
+
// Only BUILT_IN users have a rotatable password. For IAM
|
|
83
|
+
// users the type is fixed at insert and there's nothing
|
|
84
|
+
// mutable to sync. We don't store/compare BUILT_IN
|
|
85
|
+
// passwords (they're write-only on the API side) — if the
|
|
86
|
+
// user explicitly supplies one we re-issue the update so
|
|
87
|
+
// a rotated `Redacted` propagates, but we can't detect
|
|
88
|
+
// drift here so this is a deliberate "user is source of
|
|
89
|
+
// truth" path.
|
|
90
|
+
!isIamUserType(desiredType) &&
|
|
91
|
+
news.password) {
|
|
92
|
+
const op = yield* updateUsers({
|
|
93
|
+
project: news.project,
|
|
94
|
+
instance: news.instance,
|
|
95
|
+
name: news.name,
|
|
96
|
+
...(news.host !== undefined ? { host: news.host } : {}),
|
|
97
|
+
body: {
|
|
98
|
+
name: news.name,
|
|
99
|
+
...(news.host !== undefined ? { host: news.host } : {}),
|
|
100
|
+
type: desiredType,
|
|
101
|
+
password: Redacted.value(news.password),
|
|
102
|
+
},
|
|
103
|
+
}).pipe(Effect.catchTag("BadRequest", reshapeBadRequest("User", "update")));
|
|
104
|
+
if (op.name)
|
|
105
|
+
yield* awaitOperation(news.project, op.name, session);
|
|
106
|
+
}
|
|
107
|
+
// observed is non-undefined here: either we just created it
|
|
108
|
+
// and re-observed, or we found it pre-existing. If somehow
|
|
109
|
+
// listUsers still returns nothing (eventual consistency
|
|
110
|
+
// window), synthesise a minimal attribute set from inputs.
|
|
111
|
+
if (!observed) {
|
|
112
|
+
return {
|
|
113
|
+
name: news.name,
|
|
114
|
+
instance: news.instance,
|
|
115
|
+
project: news.project,
|
|
116
|
+
host: desiredHost,
|
|
117
|
+
type: desiredType,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return toAttributes(observed, {
|
|
121
|
+
project: news.project,
|
|
122
|
+
instance: news.instance,
|
|
123
|
+
name: news.name,
|
|
124
|
+
host: desiredHost,
|
|
125
|
+
});
|
|
126
|
+
}),
|
|
127
|
+
delete: Effect.fn(function* ({ output, session }) {
|
|
128
|
+
yield* deleteUsers({
|
|
129
|
+
project: output.project,
|
|
130
|
+
instance: output.instance,
|
|
131
|
+
name: output.name,
|
|
132
|
+
...(output.host && output.host !== "%" ? { host: output.host } : {}),
|
|
133
|
+
}).pipe(Effect.flatMap((op) => op.name
|
|
134
|
+
? awaitOperation(output.project, op.name, session)
|
|
135
|
+
: Effect.succeed(op)), Effect.catchTag("NotFound", () => Effect.void), Effect.catchTag("BadRequest", reshapeBadRequest("User", "delete")));
|
|
136
|
+
}),
|
|
137
|
+
read: Effect.fn(function* ({ output, olds }) {
|
|
138
|
+
const project = output?.project ?? olds?.project;
|
|
139
|
+
const instance = output?.instance ?? olds?.instance;
|
|
140
|
+
const name = output?.name ?? olds?.name;
|
|
141
|
+
if (!project || !instance || !name)
|
|
142
|
+
return undefined;
|
|
143
|
+
const host = output?.host ?? olds?.host;
|
|
144
|
+
const observed = yield* observe(project, instance, name, host);
|
|
145
|
+
if (!observed)
|
|
146
|
+
return undefined;
|
|
147
|
+
return toAttributes(observed, {
|
|
148
|
+
project,
|
|
149
|
+
instance,
|
|
150
|
+
name,
|
|
151
|
+
host: host ?? "%",
|
|
152
|
+
});
|
|
153
|
+
}),
|
|
154
|
+
};
|
|
155
|
+
}));
|
|
156
|
+
//# sourceMappingURL=User.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.js","sourceRoot":"","sources":["../../src/Sqladmin/User.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ConfigError } from "@distilled.cloud/gcp";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { type SqlUserType } from "./Types.ts";
|
|
4
|
+
/**
|
|
5
|
+
* Validate a Cloud SQL instance name. Returns a `ConfigError` on the
|
|
6
|
+
* failure channel if the name is invalid, otherwise succeeds.
|
|
7
|
+
*
|
|
8
|
+
* @see https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/instances#SqlInstance
|
|
9
|
+
*/
|
|
10
|
+
export declare const validateInstanceName: (name: string) => Effect.Effect<void, ConfigError>;
|
|
11
|
+
/**
|
|
12
|
+
* Validate a Cloud SQL database name (1–64 chars, non-empty).
|
|
13
|
+
*
|
|
14
|
+
* @see https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/databases#Database
|
|
15
|
+
*/
|
|
16
|
+
export declare const validateDatabaseName: (name: string) => Effect.Effect<void, ConfigError>;
|
|
17
|
+
/**
|
|
18
|
+
* Validate a Cloud SQL user shape — primarily that the password/type
|
|
19
|
+
* combination is consistent:
|
|
20
|
+
*
|
|
21
|
+
* - `BUILT_IN` users MUST carry a password.
|
|
22
|
+
* - Any IAM user type (`CLOUD_IAM_USER`, `CLOUD_IAM_SERVICE_ACCOUNT`,
|
|
23
|
+
* `CLOUD_IAM_GROUP*`, `ENTRAID_USER`) MUST NOT carry a password —
|
|
24
|
+
* the IAM principal is authenticated by token exchange.
|
|
25
|
+
*
|
|
26
|
+
* Other types from the open tail of the enum (forward-compat) skip
|
|
27
|
+
* the password check.
|
|
28
|
+
*
|
|
29
|
+
* @see https://cloud.google.com/sql/docs/postgres/users
|
|
30
|
+
* @see https://cloud.google.com/sql/docs/postgres/iam-authentication
|
|
31
|
+
*/
|
|
32
|
+
export declare const validateUser: (args: {
|
|
33
|
+
name: string;
|
|
34
|
+
type: SqlUserType | undefined;
|
|
35
|
+
hasPassword: boolean;
|
|
36
|
+
}) => Effect.Effect<void, ConfigError>;
|
|
37
|
+
/**
|
|
38
|
+
* Reshape a `BadRequest` from a Cloud SQL Admin create/patch/delete
|
|
39
|
+
* into a `ConfigError` with a remediation hint for the top failure
|
|
40
|
+
* modes:
|
|
41
|
+
*
|
|
42
|
+
* 1. **Billing not enabled** — Cloud SQL refuses to provision against
|
|
43
|
+
* a project without billing. Common right after `ApiEnable` lands
|
|
44
|
+
* but before billing reconciles in a fresh project.
|
|
45
|
+
* 2. **Private network not set up** — `privateNetwork` references a
|
|
46
|
+
* VPC that has no PSA peering for `servicenetworking.googleapis.com`.
|
|
47
|
+
* Surface the precondition.
|
|
48
|
+
* 3. **Invalid tier** — typo'd `db-custom-…` or one outside the
|
|
49
|
+
* project's allow-list. Pass the underlying message through with
|
|
50
|
+
* a pointer to the tier catalogue.
|
|
51
|
+
*
|
|
52
|
+
* Anything else propagates verbatim — the underlying GCP message is
|
|
53
|
+
* usually clear enough.
|
|
54
|
+
*
|
|
55
|
+
* @see https://cloud.google.com/sql/docs/postgres/instance-settings
|
|
56
|
+
*/
|
|
57
|
+
export declare const reshapeBadRequest: (kind: "Instance" | "Database" | "User", op: "insert" | "patch" | "update" | "delete") => (e: {
|
|
58
|
+
message?: string;
|
|
59
|
+
}) => Effect.Effect<never, ConfigError>;
|
|
60
|
+
//# sourceMappingURL=Validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Validation.d.ts","sourceRoot":"","sources":["../../src/Sqladmin/Validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAgC7D;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CASjC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,KACX,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAOjC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;IAC9B,WAAW,EAAE,OAAO,CAAC;CACtB,KAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAiBlC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,GAC3B,MAAM,UAAU,GAAG,UAAU,GAAG,MAAM,EAAE,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,MACpF,GAAG;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAkB1D,CAAC"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ConfigError } from "@distilled.cloud/gcp";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { isIamUserType } from "./Types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Plan-time validation for Cloud SQL resource props. Failing fast with
|
|
6
|
+
* a typed `ConfigError` is friendlier than letting the user wait for
|
|
7
|
+
* Cloud SQL to reject the request, and unlike server-side errors these
|
|
8
|
+
* surface before any state mutation happens.
|
|
9
|
+
*
|
|
10
|
+
* @see https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/instances
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Instance name regex per
|
|
14
|
+
* <https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/instances#SqlInstance>:
|
|
15
|
+
* 1–98 lowercase letters/digits/hyphens, must begin with a letter and
|
|
16
|
+
* not end with a hyphen.
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
const INSTANCE_NAME_RE = /^[a-z]([-a-z0-9]*[a-z0-9])?$/;
|
|
21
|
+
/**
|
|
22
|
+
* Database name length cap per
|
|
23
|
+
* <https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/databases#Database>:
|
|
24
|
+
* 1–64 chars. Postgres has additional character constraints (no NUL,
|
|
25
|
+
* no embedded quotes) but the API rejects those, and they'd require
|
|
26
|
+
* dialect-specific parsing here.
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
const DATABASE_NAME_MAX = 64;
|
|
31
|
+
/**
|
|
32
|
+
* Validate a Cloud SQL instance name. Returns a `ConfigError` on the
|
|
33
|
+
* failure channel if the name is invalid, otherwise succeeds.
|
|
34
|
+
*
|
|
35
|
+
* @see https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/instances#SqlInstance
|
|
36
|
+
*/
|
|
37
|
+
export const validateInstanceName = (name) => {
|
|
38
|
+
if (INSTANCE_NAME_RE.test(name) && name.length >= 1 && name.length <= 98) {
|
|
39
|
+
return Effect.void;
|
|
40
|
+
}
|
|
41
|
+
return Effect.fail(new ConfigError({
|
|
42
|
+
message: `Cloud SQL instance name ${JSON.stringify(name)} is invalid: must match /^[a-z]([-a-z0-9]*[a-z0-9])?$/ (1–98 lowercase letters/digits/hyphens, start with letter, no trailing hyphen).`,
|
|
43
|
+
}));
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Validate a Cloud SQL database name (1–64 chars, non-empty).
|
|
47
|
+
*
|
|
48
|
+
* @see https://cloud.google.com/sql/docs/postgres/admin-api/rest/v1/databases#Database
|
|
49
|
+
*/
|
|
50
|
+
export const validateDatabaseName = (name) => {
|
|
51
|
+
if (name.length >= 1 && name.length <= DATABASE_NAME_MAX)
|
|
52
|
+
return Effect.void;
|
|
53
|
+
return Effect.fail(new ConfigError({
|
|
54
|
+
message: `Cloud SQL database name ${JSON.stringify(name)} is invalid: must be 1–${DATABASE_NAME_MAX} characters.`,
|
|
55
|
+
}));
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Validate a Cloud SQL user shape — primarily that the password/type
|
|
59
|
+
* combination is consistent:
|
|
60
|
+
*
|
|
61
|
+
* - `BUILT_IN` users MUST carry a password.
|
|
62
|
+
* - Any IAM user type (`CLOUD_IAM_USER`, `CLOUD_IAM_SERVICE_ACCOUNT`,
|
|
63
|
+
* `CLOUD_IAM_GROUP*`, `ENTRAID_USER`) MUST NOT carry a password —
|
|
64
|
+
* the IAM principal is authenticated by token exchange.
|
|
65
|
+
*
|
|
66
|
+
* Other types from the open tail of the enum (forward-compat) skip
|
|
67
|
+
* the password check.
|
|
68
|
+
*
|
|
69
|
+
* @see https://cloud.google.com/sql/docs/postgres/users
|
|
70
|
+
* @see https://cloud.google.com/sql/docs/postgres/iam-authentication
|
|
71
|
+
*/
|
|
72
|
+
export const validateUser = (args) => {
|
|
73
|
+
const type = args.type ?? "BUILT_IN";
|
|
74
|
+
if (isIamUserType(type) && args.hasPassword) {
|
|
75
|
+
return Effect.fail(new ConfigError({
|
|
76
|
+
message: `Cloud SQL user ${JSON.stringify(args.name)} has type ${JSON.stringify(type)} (IAM-backed) but a password was supplied. IAM users authenticate via the Cloud SQL Auth Proxy / IAM token exchange — drop the \`password\` field.`,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
if (type === "BUILT_IN" && !args.hasPassword) {
|
|
80
|
+
return Effect.fail(new ConfigError({
|
|
81
|
+
message: `Cloud SQL user ${JSON.stringify(args.name)} has type "BUILT_IN" but no password was supplied. BUILT_IN users require a password — pass \`password: Redacted.make("...")\` or switch to an IAM type.`,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
return Effect.void;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Reshape a `BadRequest` from a Cloud SQL Admin create/patch/delete
|
|
88
|
+
* into a `ConfigError` with a remediation hint for the top failure
|
|
89
|
+
* modes:
|
|
90
|
+
*
|
|
91
|
+
* 1. **Billing not enabled** — Cloud SQL refuses to provision against
|
|
92
|
+
* a project without billing. Common right after `ApiEnable` lands
|
|
93
|
+
* but before billing reconciles in a fresh project.
|
|
94
|
+
* 2. **Private network not set up** — `privateNetwork` references a
|
|
95
|
+
* VPC that has no PSA peering for `servicenetworking.googleapis.com`.
|
|
96
|
+
* Surface the precondition.
|
|
97
|
+
* 3. **Invalid tier** — typo'd `db-custom-…` or one outside the
|
|
98
|
+
* project's allow-list. Pass the underlying message through with
|
|
99
|
+
* a pointer to the tier catalogue.
|
|
100
|
+
*
|
|
101
|
+
* Anything else propagates verbatim — the underlying GCP message is
|
|
102
|
+
* usually clear enough.
|
|
103
|
+
*
|
|
104
|
+
* @see https://cloud.google.com/sql/docs/postgres/instance-settings
|
|
105
|
+
*/
|
|
106
|
+
export const reshapeBadRequest = (kind, op) => (e) => {
|
|
107
|
+
const underlying = e.message ?? `unknown 400 from ${kind} ${op}`;
|
|
108
|
+
let hint = "";
|
|
109
|
+
if (/billing/i.test(underlying)) {
|
|
110
|
+
hint =
|
|
111
|
+
" Cloud SQL requires billing to be enabled on the project. Attach a billing account (see `GCP.Project({ billingAccount })`) and retry.";
|
|
112
|
+
}
|
|
113
|
+
else if (/private.*service.*access|servicenetworking|peering/i.test(underlying)) {
|
|
114
|
+
hint =
|
|
115
|
+
" The host VPC needs a Private Services Access (PSA) peering with `servicenetworking.googleapis.com` before a private-IP Cloud SQL instance can attach. Provision a `GCP.GlobalAddress` (purpose=VPC_PEERING) and a `GCP.PsaConnection` on the VPC first.";
|
|
116
|
+
}
|
|
117
|
+
else if (/tier|machine.*type/i.test(underlying)) {
|
|
118
|
+
hint =
|
|
119
|
+
" Check the tier identifier against https://cloud.google.com/sql/docs/postgres/instance-settings — and remember that Enterprise Plus tiers (`db-perf-optimized-*`) require `edition: \"ENTERPRISE_PLUS\"`.";
|
|
120
|
+
}
|
|
121
|
+
return Effect.fail(new ConfigError({
|
|
122
|
+
message: `Cloud SQL ${kind} ${op} rejected: ${underlying}.${hint}`,
|
|
123
|
+
}));
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=Validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Validation.js","sourceRoot":"","sources":["../../src/Sqladmin/Validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { SqlDatabase, SqlDatabaseProvider } from "./Database.ts";
|
|
2
|
+
export type { SqlDatabaseAttributes, SqlDatabaseProps } from "./Database.ts";
|
|
3
|
+
export { SqlInstance, SqlInstanceProvider } from "./Instance.ts";
|
|
4
|
+
export type { SqlInstanceAttributes, SqlInstanceIpAddress, SqlInstanceProps, } from "./Instance.ts";
|
|
5
|
+
export { isIamUserType } from "./Types.ts";
|
|
6
|
+
export type { PostgresVersion, SqlActivationPolicy, SqlAvailabilityType, SqlDataDiskType, SqlEdition, SqlUserType, } from "./Types.ts";
|
|
7
|
+
export { SqlUser, SqlUserProvider } from "./User.ts";
|
|
8
|
+
export type { SqlUserAttributes, SqlUserProps } from "./User.ts";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Sqladmin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACjE,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,UAAU,EACV,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/Sqladmin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAMjE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAS3C,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
package/lib/Tags.d.ts
CHANGED
|
@@ -28,25 +28,25 @@ export declare const gcpInternalLabels: (id: string) => Effect.Effect<{
|
|
|
28
28
|
alchemy_app: string;
|
|
29
29
|
alchemy_stage: string;
|
|
30
30
|
alchemy_id: string;
|
|
31
|
-
}, never,
|
|
31
|
+
}, never, Stack | Stage>;
|
|
32
32
|
/**
|
|
33
33
|
* True when `labels` contains all three alchemy-internal keys with
|
|
34
34
|
* values matching this stack/stage/id. Used in `read` to decide
|
|
35
35
|
* adoption ownership.
|
|
36
36
|
*/
|
|
37
|
-
export declare const hasAlchemyLabels: (id: string, labels: Record<string, string> | undefined) => Effect.Effect<boolean, never,
|
|
37
|
+
export declare const hasAlchemyLabels: (id: string, labels: Record<string, string> | undefined) => Effect.Effect<boolean, never, Stack | Stage>;
|
|
38
38
|
export { LABEL_KEY_APP as ALCHEMY_LABEL_APP, LABEL_KEY_STAGE as ALCHEMY_LABEL_STAGE, LABEL_KEY_ID as ALCHEMY_LABEL_ID, };
|
|
39
39
|
/**
|
|
40
40
|
* Compose `[alchemy:...] <userDescription?>`. Pass through to the
|
|
41
41
|
* resource's `description` body field at create time.
|
|
42
42
|
*/
|
|
43
|
-
export declare const gcpAlchemyDescription: (id: string, userDescription?: string | undefined) => Effect.Effect<string, never,
|
|
43
|
+
export declare const gcpAlchemyDescription: (id: string, userDescription?: string | undefined) => Effect.Effect<string, never, Stack | Stage>;
|
|
44
44
|
/**
|
|
45
45
|
* True when `description` begins with an alchemy marker matching this
|
|
46
46
|
* stack/stage/id. Used in `read` for resources that lack a labels
|
|
47
47
|
* field.
|
|
48
48
|
*/
|
|
49
|
-
export declare const descriptionHasAlchemyMarker: (id: string, description: string | undefined) => Effect.Effect<boolean, never,
|
|
49
|
+
export declare const descriptionHasAlchemyMarker: (id: string, description: string | undefined) => Effect.Effect<boolean, never, Stack | Stage>;
|
|
50
50
|
/**
|
|
51
51
|
* Strip the leading alchemy marker (if any) and return what remains —
|
|
52
52
|
* used when surfacing the user-visible description back through
|
package/lib/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export { Credentials, fromADC, fromAuthProvider } from "./Auth/Credentials.ts";
|
|
2
2
|
export { GCP_AUTH_PROVIDER_NAME, GCPAuth, } from "./Auth/AuthProvider.ts";
|
|
3
3
|
export type { GCPAuthConfig, GCPResolvedCredentials, } from "./Auth/AuthProvider.ts";
|
|
4
|
+
export * from "./ArtifactRegistry/index.ts";
|
|
4
5
|
export * from "./CloudResourceManager/index.ts";
|
|
5
6
|
export * from "./Compute/index.ts";
|
|
6
7
|
export * from "./Container/index.ts";
|
|
7
8
|
export * from "./ManagedLustre/index.ts";
|
|
8
9
|
export * from "./Run/index.ts";
|
|
9
10
|
export * from "./ServiceNetworking/index.ts";
|
|
11
|
+
export * from "./Sqladmin/index.ts";
|
|
10
12
|
export * from "./ServiceUsage/index.ts";
|
|
11
13
|
export * from "./Providers.ts";
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.d.ts.map
CHANGED
|
@@ -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,gBAAgB,CAAC;AAC/B,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,6BAA6B,CAAC;AAC5C,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,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export { Credentials, fromADC, fromAuthProvider } from "./Auth/Credentials.js";
|
|
2
2
|
export { GCP_AUTH_PROVIDER_NAME, GCPAuth, } from "./Auth/AuthProvider.js";
|
|
3
|
+
export * from "./ArtifactRegistry/index.js";
|
|
3
4
|
export * from "./CloudResourceManager/index.js";
|
|
4
5
|
export * from "./Compute/index.js";
|
|
5
6
|
export * from "./Container/index.js";
|
|
6
7
|
export * from "./ManagedLustre/index.js";
|
|
7
8
|
export * from "./Run/index.js";
|
|
8
9
|
export * from "./ServiceNetworking/index.js";
|
|
10
|
+
export * from "./Sqladmin/index.js";
|
|
9
11
|
export * from "./ServiceUsage/index.js";
|
|
10
12
|
export * from "./Providers.js";
|
|
11
13
|
//# 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,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"}
|
|
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,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,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.
|
|
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.",
|
|
3
|
+
"version": "0.5.1",
|
|
4
|
+
"description": "GCP provider for Alchemy v2 (Effect-based IaC). Resources for Project, 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), with ADC-backed credentials and target-side IAM bindings.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"alchemy",
|
|
7
7
|
"alchemy-effect",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ConfigError } from "@distilled.cloud/gcp";
|
|
2
|
+
import * as ar from "@distilled.cloud/gcp/artifactregistry-v1";
|
|
3
|
+
import type { ScopedPlanStatusSession } from "alchemy/Cli/Cli";
|
|
4
|
+
import * as Duration from "effect/Duration";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Schedule from "effect/Schedule";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolved callable signature of `ar.getProjectsLocationsOperations`.
|
|
10
|
+
*
|
|
11
|
+
* `ar.getProjectsLocationsOperations` is itself an
|
|
12
|
+
* `Effect<callable, never, Credentials | HttpClient>`, so
|
|
13
|
+
* `Effect.Success` extracts the callable type. Using a derived alias
|
|
14
|
+
* keeps the helper aligned with the SDK without us hand-writing the
|
|
15
|
+
* input/output/error union, which would drift if the patch set changes.
|
|
16
|
+
*/
|
|
17
|
+
type GetOperations = Effect.Success<typeof ar.getProjectsLocationsOperations>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build the Artifact Registry long-running-operation polling helper.
|
|
21
|
+
*
|
|
22
|
+
* Artifact Registry `Operation`s use the standard
|
|
23
|
+
* `GoogleLongrunningOperation` shape — doneness is `.done === true`
|
|
24
|
+
* (NOT `.status === "DONE"` like the GKE Container API and NOT
|
|
25
|
+
* Sqladmin's tri-state `.status`). Verified at
|
|
26
|
+
* `vendor/distilled/packages/gcp/src/services/artifactregistry-v1.ts:308–328`.
|
|
27
|
+
*
|
|
28
|
+
* Operation names returned by `artifactregistry-v1`
|
|
29
|
+
* (`createProjectsLocationsRepositories`,
|
|
30
|
+
* `patchProjectsLocationsRepositories`,
|
|
31
|
+
* `deleteProjectsLocationsRepositories`) are already fully qualified
|
|
32
|
+
* (`projects/{p}/locations/{l}/operations/{id}`), so callers pass them
|
|
33
|
+
* straight through to `getProjectsLocationsOperations` — no qualify
|
|
34
|
+
* dance needed (unlike Container, which returns bare ids).
|
|
35
|
+
*
|
|
36
|
+
* Schedule: exponential 1s → 1.5× growth, capped per-poll at 10s via
|
|
37
|
+
* `Schedule.either`, then a hard count cap of 60 retries → ~10 min wall
|
|
38
|
+
* ceiling. Repository CRUD is fast (seconds); the only long-running
|
|
39
|
+
* case is a delete on a repository with many cached images, where AR
|
|
40
|
+
* has to GC the underlying blobs.
|
|
41
|
+
*
|
|
42
|
+
* The helper is a factory — the caller resolves
|
|
43
|
+
* `ar.getProjectsLocationsOperations` once at provider construction
|
|
44
|
+
* and passes the resulting callable here, so we never re-resolve
|
|
45
|
+
* services on each await.
|
|
46
|
+
*/
|
|
47
|
+
export const makeAwaitOperation = (getOperations: GetOperations) =>
|
|
48
|
+
Effect.fn(function* (
|
|
49
|
+
operationName: string,
|
|
50
|
+
session: ScopedPlanStatusSession,
|
|
51
|
+
) {
|
|
52
|
+
const op = yield* getOperations({ name: operationName }).pipe(
|
|
53
|
+
Effect.flatMap((current) =>
|
|
54
|
+
current.done === true
|
|
55
|
+
? Effect.succeed(current)
|
|
56
|
+
: Effect.fail({ _tag: "OperationPending" as const }),
|
|
57
|
+
),
|
|
58
|
+
Effect.retry({
|
|
59
|
+
while: (e: { _tag?: string }) => e?._tag === "OperationPending",
|
|
60
|
+
schedule: Schedule.exponential(Duration.seconds(1), 1.5).pipe(
|
|
61
|
+
Schedule.either(Schedule.spaced(Duration.seconds(10))),
|
|
62
|
+
Schedule.both(Schedule.recurs(60)),
|
|
63
|
+
Schedule.tapOutput(() =>
|
|
64
|
+
session.note(
|
|
65
|
+
`Waiting for Artifact Registry operation ${operationName}…`,
|
|
66
|
+
),
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
if (op.error) {
|
|
72
|
+
return yield* new ConfigError({
|
|
73
|
+
message: `Artifact Registry operation ${operationName} failed: ${
|
|
74
|
+
op.error.message ?? JSON.stringify(op.error)
|
|
75
|
+
}`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return op;
|
|
79
|
+
});
|