@cyc-seattle/platform 1.1.0 → 1.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.
- package/README.md +19 -17
- package/dist/kubernetes.d.ts +1 -1
- package/dist/kubernetes.js +1 -1
- package/dist/outputs.js +3 -3
- package/dist/scheduled-job.d.ts +5 -0
- package/dist/scheduled-job.js +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -6,19 +6,19 @@ app owns. The apps that run on it live in their own repos, such as
|
|
|
6
6
|
|
|
7
7
|
## Stacks
|
|
8
8
|
|
|
9
|
-
| Stack | Holds
|
|
10
|
-
| ------------------------------- |
|
|
11
|
-
| [`bootstrap`](stacks/bootstrap) | Service accounts and project IAM, the Pulumi state buckets and KMS keys, and the org's GitHub repos and settings
|
|
12
|
-
| [`
|
|
13
|
-
| [`
|
|
14
|
-
| [`
|
|
9
|
+
| Stack | Holds | Applied by |
|
|
10
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| [`bootstrap`](stacks/bootstrap) | Service accounts and project IAM, the Pulumi state buckets and KMS keys, and the org's GitHub repos and settings | A human with project Owner and `gh` `admin:org`. Never CI. |
|
|
12
|
+
| [`infra`](stacks/infra) | The VPC, the cluster subnet and its Cloud NAT on a static address, the private services peering, the prod and dev Cloud SQL instances, the DNS zones, Artifact Registry, the GKE Autopilot cluster, the shared Gateway, its address, certificates, and dev's DNS records, the `dev` and `prod` namespaces with their quotas and network policies, dev's RBAC, the prod Job alerts, and each environment's `platform/outputs.json` | `deploy-runner`, from Actions after approval. A deployer, with `mise run deploy`, when breaking glass. |
|
|
13
|
+
| [`smoke`](stacks/smoke) | One `WebService` in dev, at `smoke.dev.cycsail.team`, for the integration tests | `deploy-dev`, from Actions on every PR. A deployer, with `PULUMI_STACK=dev mise run deploy`, when breaking glass. |
|
|
14
|
+
| [`backend`](stacks/backend) | Directus, with the schema apply from `schemas/`, its roles, policies, and permission rules, the sync machine users, its OIDC client, and MCP, and the clubspot-sync and gsuite-sync CronJobs, with dev's Clubspot and Workspace fakes | `deploy-dev`, from Actions on every PR. Dev only, until prod cuts over (cyc-seattle/workplace#6 step 6). |
|
|
15
15
|
|
|
16
16
|
## @cyc-seattle/platform
|
|
17
17
|
|
|
18
18
|
`src/` is the `@cyc-seattle/platform` package, which the stacks here and the apps' stacks import:
|
|
19
19
|
|
|
20
20
|
- `getPlatformOutputs()` reads an environment's `platform/outputs.json` from its state bucket. The
|
|
21
|
-
`
|
|
21
|
+
`infra` stack writes it. It holds the environment's namespace and domain, the Gateway and its
|
|
22
22
|
address (`gatewayAddress`, for an app's own DNS records), and the Cloud SQL instance's connection
|
|
23
23
|
name and private IP (`sqlHost`). Dev stacks can't read prod's backend, so this replaces a
|
|
24
24
|
StackReference for platform values (cyc-seattle/isthmia#215).
|
|
@@ -47,7 +47,9 @@ releases each merge to `main` with semantic-release, versioned by the convention
|
|
|
47
47
|
|
|
48
48
|
Every app runs on the one GKE Autopilot cluster, in its environment's namespace. Prod runs on
|
|
49
49
|
regular capacity, and dev on Spot pods. isthmia's apps (Authentik, Directus, the portal, and the
|
|
50
|
-
two syncs as CronJobs) run in both, and dev adds Mailpit and the Clubspot and Workspace fakes.
|
|
50
|
+
two syncs as CronJobs) run in both, and dev adds Mailpit and the Clubspot and Workspace fakes. In dev,
|
|
51
|
+
platform's `backend` deploys Directus, the syncs, and the fakes, and dev's sync CronJobs are
|
|
52
|
+
suspended: the integration features and `mise run reset` run them as Jobs (cyc-seattle/isthmia#271).
|
|
51
53
|
|
|
52
54
|
| Stack | Namespace | Domain | Deploy identity |
|
|
53
55
|
| ------ | --------- | ------------------ | --------------- |
|
|
@@ -56,19 +58,19 @@ two syncs as CronJobs) run in both, and dev adds Mailpit and the Clubspot and Wo
|
|
|
56
58
|
|
|
57
59
|
Each namespace denies ingress from other namespaces except the load balancer's. Dev also admits the
|
|
58
60
|
Kubernetes API's service proxy, which the integration tests use, and denies egress to the mail ports
|
|
59
|
-
(25, 465, 587) outside the cluster. `
|
|
61
|
+
(25, 465, 587) outside the cluster. `infra` writes dev's A records; each prod app writes its own,
|
|
60
62
|
pointing at `gatewayAddress`.
|
|
61
63
|
|
|
62
64
|
`deploy-dev` reads Secret Manager secrets whose IDs start with `dev-`. Read them with
|
|
63
65
|
`gcp.secretmanager.getSecretVersionAccess`, which needs only the accessor role.
|
|
64
66
|
|
|
65
|
-
`
|
|
66
|
-
`cyc-dev`, for each name in `
|
|
67
|
+
`infra` creates each app's dev database, user, and password secret (`dev-db-<name>-password`) on
|
|
68
|
+
`cyc-dev`, for each name in `infra:devDatabases`. To add an app's dev database, add its name there in a
|
|
67
69
|
platform PR: Cloud SQL ignores IAM conditions on its user and database calls, so `deploy-dev` can't
|
|
68
70
|
hold a role scoped to `cyc-dev`.
|
|
69
71
|
|
|
70
72
|
A Job that fails in `prod`, or a CronJob there with no successful Job in 3 hours, raises an alert.
|
|
71
|
-
It emails each address in `
|
|
73
|
+
It emails each address in `infra:alertEmails`, a secret in the stack's config, which holds the
|
|
72
74
|
maintainer's address (#20).
|
|
73
75
|
|
|
74
76
|
## Setup
|
|
@@ -100,11 +102,11 @@ GitHub environment that holds its identity and state bucket:
|
|
|
100
102
|
|
|
101
103
|
`prod-preview` runs `mise run preview` as `deploy-runner` and writes the diff to the job summary.
|
|
102
104
|
`prod` runs `mise run deploy` on the same commit once approved. It runs only when the preview shows
|
|
103
|
-
changes in `
|
|
105
|
+
changes in `infra`, so a merge that changes nothing in prod asks for no approval. A PR from a fork gets no OIDC token,
|
|
104
106
|
so its `dev` job is skipped.
|
|
105
107
|
|
|
106
|
-
`PULUMI_STACK` picks what the tasks run (`scripts/each-stack`): `dev` is `smoke`,
|
|
107
|
-
`
|
|
108
|
+
`PULUMI_STACK` picks what the tasks run (`scripts/each-stack`): `dev` is `smoke`, then `backend`, and
|
|
109
|
+
`prod` is `infra`. `integration` is dev only.
|
|
108
110
|
|
|
109
111
|
`bootstrap` is never deployed from Actions (cyc-seattle/isthmia#118). It holds the identities that
|
|
110
112
|
CI assumes, and a human with project Owner and `gh` `admin:org` applies it with the tasks below.
|
|
@@ -121,8 +123,8 @@ mise run deploy
|
|
|
121
123
|
PULUMI_STACK=dev mise run deploy
|
|
122
124
|
```
|
|
123
125
|
|
|
124
|
-
`mise run deploy` applies `
|
|
125
|
-
|
|
126
|
+
`mise run deploy` applies `infra`. isthmia's prod stacks read its outputs through a StackReference.
|
|
127
|
+
Dev stacks can't read prod state, so `infra` writes each environment's platform
|
|
126
128
|
values to `platform/outputs.json` in that environment's state bucket.
|
|
127
129
|
|
|
128
130
|
`bootstrap`'s GitHub provider uses `gh auth token`, so log `gh` in as a cyc-seattle org owner with
|
package/dist/kubernetes.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface ClusterProviderArgs {
|
|
|
11
11
|
/**
|
|
12
12
|
* A Kubernetes provider for the environment's cluster. Its kubeconfig runs gke-gcloud-auth-plugin
|
|
13
13
|
* with Application Default Credentials, so it is the same on every run, unlike one that embeds an
|
|
14
|
-
* access token. The plugin must be on PATH: `mise run install` adds it. The
|
|
14
|
+
* access token. The plugin must be on PATH: `mise run install` adds it. The infra stack passes
|
|
15
15
|
* only the endpoint, because it writes the rest.
|
|
16
16
|
*/
|
|
17
17
|
export declare function clusterProvider(platform: pulumi.Input<{
|
package/dist/kubernetes.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as pulumi from "@pulumi/pulumi";
|
|
|
3
3
|
/**
|
|
4
4
|
* A Kubernetes provider for the environment's cluster. Its kubeconfig runs gke-gcloud-auth-plugin
|
|
5
5
|
* with Application Default Credentials, so it is the same on every run, unlike one that embeds an
|
|
6
|
-
* access token. The plugin must be on PATH: `mise run install` adds it. The
|
|
6
|
+
* access token. The plugin must be on PATH: `mise run install` adds it. The infra stack passes
|
|
7
7
|
* only the endpoint, because it writes the rest.
|
|
8
8
|
*/
|
|
9
9
|
export function clusterProvider(platform, name = "cluster", opts, args = {}) {
|
package/dist/outputs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as gcp from "@pulumi/gcp";
|
|
2
2
|
import * as pulumi from "@pulumi/pulumi";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
// The
|
|
4
|
+
// The infra stack writes this object to each environment's state bucket, because a StackReference
|
|
5
5
|
// can't cross from a dev stack into prod's backend (cyc-seattle/isthmia#215).
|
|
6
6
|
export const platformOutputsSchema = z.object({
|
|
7
7
|
project: z.string().min(1),
|
|
@@ -49,10 +49,10 @@ export async function readPlatformOutputs(args = {}) {
|
|
|
49
49
|
({ content } = await gcp.storage.getBucketObjectContent({ bucket, name: outputsObject }));
|
|
50
50
|
}
|
|
51
51
|
catch (e) {
|
|
52
|
-
throw new Error(`Can't read ${source}. The
|
|
52
|
+
throw new Error(`Can't read ${source}. The infra stack writes it.\n${e.message}`);
|
|
53
53
|
}
|
|
54
54
|
if (!content) {
|
|
55
|
-
throw new Error(`${source} is empty. The
|
|
55
|
+
throw new Error(`${source} is empty. The infra stack writes it.`);
|
|
56
56
|
}
|
|
57
57
|
return parsePlatformOutputs(content, source);
|
|
58
58
|
}
|
package/dist/scheduled-job.d.ts
CHANGED
|
@@ -9,6 +9,11 @@ export interface ScheduledJobArgs extends PodArgs {
|
|
|
9
9
|
activeDeadlineSeconds?: number;
|
|
10
10
|
/** How many times a failed run retries. Defaults to 0, so the next scheduled run is the retry. */
|
|
11
11
|
backoffLimit?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Stops the scheduled runs, while a Job made from the CronJob still runs. Defaults to false. Unset,
|
|
14
|
+
* it stays out of the spec, so the CronJobs deployed before it existed show no diff.
|
|
15
|
+
*/
|
|
16
|
+
suspend?: boolean;
|
|
12
17
|
}
|
|
13
18
|
/**
|
|
14
19
|
* A CronJob with the platform's secure defaults, and a Kubernetes ServiceAccount, both named `name`.
|
package/dist/scheduled-job.js
CHANGED
|
@@ -22,6 +22,7 @@ export class ScheduledJob extends pulumi.ComponentResource {
|
|
|
22
22
|
schedule: args.schedule,
|
|
23
23
|
timeZone: args.timeZone ?? "America/Los_Angeles",
|
|
24
24
|
concurrencyPolicy: "Forbid",
|
|
25
|
+
...(args.suspend !== undefined && { suspend: args.suspend }),
|
|
25
26
|
jobTemplate: {
|
|
26
27
|
metadata: { labels },
|
|
27
28
|
spec: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyc-seattle/platform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Pulumi components for CYC's shared Google Cloud platform: WebService, the platform outputs reader, and the stacks that build the platform",
|
|
5
5
|
"author": "CYC Community Sailing Center",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@amiceli/vitest-cucumber": "7.0.0",
|
|
48
48
|
"@cyc-seattle/ts-config": "^0.1.2",
|
|
49
49
|
"@pulumi/command": "1.2.1",
|
|
50
|
+
"@pulumi/docker-build": "0.0.22",
|
|
50
51
|
"@pulumi/gcp": "10.2.0",
|
|
51
52
|
"@pulumi/github": "6.15.0",
|
|
52
53
|
"@pulumi/kubernetes": "4.34.2",
|