@ossy/deployment-tools 3.0.9 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +156 -118
- package/jest.config.js +5 -0
- package/package.json +6 -4
- package/src/config/platform-config.js +16 -16
- package/src/ecs/platform-ecs-services.js +209 -0
- package/src/ecs/platform-ecs-services.spec.js +163 -0
- package/src/edge/platform-edge-domains.js +101 -0
- package/src/edge/platform-edge-domains.spec.js +87 -0
- package/src/index.js +45 -1
- package/src/infrastructure/dns-stack.js +47 -3
- package/src/infrastructure/dns-stack.spec.js +15 -0
- package/src/infrastructure/platform-ci-stack.js +151 -0
- package/src/infrastructure/platform-ecs-stack.js +349 -0
- package/src/infrastructure/platform-edge-stack.js +213 -0
- package/src/infrastructure/platform-secrets-stack.js +103 -0
- package/src/infrastructure/platform-stage.js +32 -5
- package/src/infrastructure/storage-static-stack.js +4 -5
- package/src/secrets/platform-secret-services.js +32 -0
- package/src/secrets/platform-secret-services.spec.js +40 -0
- package/src/secrets/sync-platform-secrets.js +136 -0
- package/src/services/platform-services.js +94 -0
- package/src/services/platform-services.spec.js +80 -0
- package/src/template/platform-template.js +5 -5
- package/src/infrastructure/container-deployment-target/aws-profile.js +0 -25
- package/src/infrastructure/container-deployment-target/caddy.service.js +0 -117
- package/src/infrastructure/container-deployment-target/container-deployment-target.js +0 -222
- package/src/infrastructure/container-deployment-target/container-service.js +0 -162
- package/src/infrastructure/container-deployment-target/index.js +0 -3
- package/src/infrastructure/container-deployment-target/ossy-api.service.js +0 -54
- package/src/infrastructure/container-deployment-target/ossy-runtime.service.js +0 -49
- package/src/infrastructure/container-deployment-target/user-data-commands.js +0 -31
- package/src/infrastructure/deployment-target-stack.js +0 -53
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [3.4.0](https://github.com/ossy-se/ossy/compare/v3.3.0...v3.4.0) (2026-08-14)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **deployment-tools:** add ECS container /health probes ([#589](https://github.com/ossy-se/ossy/issues/589)) ([0761f19](https://github.com/ossy-se/ossy/commit/0761f19a695f98470fccdcd9fc90ae0af288d7f3)), closes [#553](https://github.com/ossy-se/ossy/issues/553) [#553](https://github.com/ossy-se/ossy/issues/553)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# [3.1.0](https://github.com/ossy-se/ossy/compare/v3.0.9...v3.1.0) (2026-08-12)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **deployment-tools:** add CloudFront WAF and ACM edge in front of ALB ([#574](https://github.com/ossy-se/ossy/issues/574)) ([2dd1a69](https://github.com/ossy-se/ossy/commit/2dd1a69f4facfcbcc0e30c931915e6d58c916ad7))
|
|
18
|
+
* **deployment-tools:** add ECS Fargate and ALB for platform services ([#573](https://github.com/ossy-se/ossy/issues/573)) ([42ef998](https://github.com/ossy-se/ossy/commit/42ef9986a1f96b422f0351b311e906dadd4db26b)), closes [#557](https://github.com/ossy-se/ossy/issues/557) [#572](https://github.com/ossy-se/ossy/issues/572) [#560](https://github.com/ossy-se/ossy/issues/560)
|
|
19
|
+
* **deployment-tools:** add per-service Secrets Manager secrets ([#572](https://github.com/ossy-se/ossy/issues/572)) ([021ad55](https://github.com/ossy-se/ossy/commit/021ad5535d20722c819c154f41239bf8e583b9c3)), closes [#555](https://github.com/ossy-se/ossy/issues/555)
|
|
20
|
+
* **deployment-tools:** cut over DNS to CloudFront and retire EC2/Caddy ([#570](https://github.com/ossy-se/ossy/issues/570)) ([99c4323](https://github.com/ossy-se/ossy/commit/99c43236adfee4ea68534f3245d2d9d420c51ce4)), closes [#560](https://github.com/ossy-se/ossy/issues/560) [#560](https://github.com/ossy-se/ossy/issues/560)
|
|
21
|
+
|
|
22
|
+
|
|
6
23
|
## 3.0.9 (2026-08-11)
|
|
7
24
|
|
|
8
25
|
**Note:** Version bump only for package @ossy/deployment-tools
|
package/README.md
CHANGED
|
@@ -1,45 +1,41 @@
|
|
|
1
1
|
# @ossy/deployment-tools
|
|
2
2
|
|
|
3
|
-
Infrastructure and CDK tooling for the Ossy platform
|
|
3
|
+
Infrastructure and CDK tooling for the Ossy platform.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Architecture** (epic #553): **CloudFront → WAF → ALB → ECS Fargate**, with per-service Secrets Manager and GitHub Actions rollouts via OIDC. EC2 + Caddy + EIP are **removed from CDK** (#560).
|
|
6
6
|
|
|
7
7
|
## Architecture
|
|
8
8
|
|
|
9
9
|
```
|
|
10
|
-
Internet →
|
|
11
|
-
├──
|
|
12
|
-
|
|
13
|
-
├──
|
|
14
|
-
└──
|
|
15
|
-
└── loads build from CMS on first request
|
|
10
|
+
Internet → CloudFront (ACM TLS, us-east-1) → WAF → ALB (:80)
|
|
11
|
+
├── host rules from platforms.json `services[].hosts|domain`
|
|
12
|
+
│ e.g. ossy.se, api.ossy.se → website-ossy Fargate service
|
|
13
|
+
├── other known hosts / *.ossy.se → platform-runtime Fargate service
|
|
14
|
+
└── default action → platform-runtime
|
|
16
15
|
```
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
- Website images include the API — **no** separate `ossy-api` service.
|
|
18
|
+
- Task definitions inject env from Secrets Manager (`{platform}/{serviceKey}`) and pull images from GHCR using `{platform}/ghcr-pull`.
|
|
19
|
+
- Health checks use `GET /health` on each ALB target group **and** each ECS container (Node `fetch` probe; no curl in `node:*-slim`).
|
|
20
|
+
- App CloudFront is **separate** from the `/media` CloudFront in `storage-static`.
|
|
21
|
+
- Viewer `Host` is forwarded to the ALB (`OriginRequestPolicy.ALL_VIEWER`) so host rules match.
|
|
22
|
+
- ACM terminates TLS at CloudFront; Caddy is not part of this path.
|
|
23
|
+
- Route53 for known domains aliases to CloudFront (A + AAAA), not an Elastic IP.
|
|
24
|
+
|
|
25
|
+
Customer nameserver / custom-domain handoff at a registrar is **out of scope** (follow-up).
|
|
19
26
|
|
|
20
27
|
## Health checks (ALB / ECS)
|
|
21
28
|
|
|
22
|
-
Every deployable Ossy HTTP image exposes an unauthenticated liveness probe at **`GET /health`** (also accepts `HEAD`). It returns `200` when the Node process can accept traffic.
|
|
29
|
+
Every deployable Ossy HTTP image exposes an unauthenticated liveness probe at **`GET /health`** (also accepts `HEAD`). It returns `200` when the Node process can accept traffic. The JSON `service` field prefers **`OSSY_SERVICE_NAME`** (set on the task definition to the service key).
|
|
23
30
|
|
|
24
31
|
| Image | How `/health` is provided |
|
|
25
32
|
|---|---|
|
|
26
33
|
| `ghcr.io/ossy-se/platform` (runtime) | Built into `@ossy/platform` `startRuntime` — does not load a CMS site |
|
|
27
34
|
| Website / app images (`website-ossy`, other `services[]`) | Built into `@ossy/platform` `startServer` (and/or a local `health.api.js` until the platform package is bumped) |
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
## Services
|
|
32
|
-
|
|
33
|
-
All services run as Docker containers managed by systemd. Service files are written to the EC2 host by CDK user data at instance creation.
|
|
36
|
+
**ALB target groups** probe `GET /health`. **ECS container health checks** run an exec-form Node one-liner (`ecsContainerHealthCheckCommand`) that `fetch`es the same path on `PORT` and aborts after `HEALTHCHECK_FETCH_TIMEOUT_MS` (4s). Do not point probes at `/`, `/status`, or authenticated routes.
|
|
34
37
|
|
|
35
|
-
|
|
36
|
-
|---|---|---|---|
|
|
37
|
-
| Caddy | `caddy-route53.service` | 80, 443 | — |
|
|
38
|
-
| Ossy API | `ossy-api.service` | 3001 | `ghcr.io/ossy-se/api:latest` |
|
|
39
|
-
| Ossy Platform | `ossy-runtime.service` | 3000 | `ghcr.io/ossy-se/platform:latest` |
|
|
40
|
-
| _configured via `services`_ | `<name>.service` | _per config_ | _per config_ |
|
|
41
|
-
|
|
42
|
-
Additional services are generated automatically from the `services` array in `platforms.json` — see the Configuration section below.
|
|
38
|
+
Task definitions also set plain env `PORT`, `OSSY_SERVICE_NAME`, `NODE_ENV`, and media bucket vars via `containerEnvironment`. Those keys are **filtered out** of Secrets Manager injection (`secretEnvKeysForEcsContainer`) so ECS cannot reject duplicate environment/secret keys.
|
|
43
39
|
|
|
44
40
|
## Configuration — `platforms.json`
|
|
45
41
|
|
|
@@ -49,101 +45,179 @@ Additional services are generated automatically from the `services` array in `pl
|
|
|
49
45
|
|---|---|---|
|
|
50
46
|
| `platformName` | Yes | Unique name, used as the CDK stage name and CloudFormation stack prefix |
|
|
51
47
|
| `awsAccountId` | Yes | AWS account ID |
|
|
52
|
-
| `
|
|
53
|
-
| `awsInstanceClass` | No | EC2 instance class (e.g. `t3`, `t4g`, `m5`). Defaults to `t3` |
|
|
54
|
-
| `awsInstanceSize` | No | EC2 instance size (e.g. `small`, `medium`, `large`). Defaults to `small` |
|
|
48
|
+
| `awsRegion` | No | Defaults to `eu-north-1` |
|
|
55
49
|
| `sesDomains` | No | Domains to configure SES email identity for |
|
|
56
|
-
| `domains` | No | Domains
|
|
57
|
-
| `services` | No | Per-platform container services (see below) |
|
|
50
|
+
| `domains` | No | Domains for Route53 aliases → CloudFront and ACM coverage. Supports wildcards (e.g. `*.ossy.se`). |
|
|
51
|
+
| `services` | No | Per-platform HTTP container services (see below) |
|
|
58
52
|
| `dnsRecords` | No | Additional DNS records by root domain (currently supports MX) |
|
|
59
|
-
| `env` | No |
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
| `env` | No | Env vars upserted into per-service Secrets Manager secrets (`npm run sync-secrets`). **Never uploaded to S3.** Values remain in git for now. |
|
|
54
|
+
| `githubDeployRepos` | Yes (for `platform-ci`) | `owner/repo` list trusted by the GitHub OIDC deploy role. Set in `platforms.json` (e.g. `ossy-se/ossy`, `ossy-se/website-ossy`). |
|
|
55
|
+
| `awsKeyPairName` / `awsInstanceClass` / `awsInstanceSize` | No | **Unused** after EC2 decommission (#560). Harmless if left in JSON. |
|
|
62
56
|
|
|
63
|
-
|
|
57
|
+
### `services` — HTTP containers on ECS
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Generates a Caddy reverse-proxy block with Route53 TLS. The container must listen on port 3000.
|
|
59
|
+
HTTP services (default) become Fargate services with ALB host rules. The container must listen on port 3000.
|
|
68
60
|
|
|
69
61
|
```json
|
|
70
62
|
"services": [
|
|
71
63
|
{
|
|
72
64
|
"name": "ossy-website-ossy",
|
|
73
65
|
"domain": "ossy.se",
|
|
74
|
-
"image": "ghcr.io/ossy-se/website-ossy:latest"
|
|
66
|
+
"image": "ghcr.io/ossy-se/website-ossy:latest",
|
|
67
|
+
"hosts": ["ossy.se", "api.ossy.se"]
|
|
75
68
|
}
|
|
76
69
|
]
|
|
77
70
|
```
|
|
78
71
|
|
|
79
72
|
| Field | Description |
|
|
80
73
|
|---|---|
|
|
81
|
-
| `name` | Unique service name —
|
|
82
|
-
| `domain` |
|
|
83
|
-
| `
|
|
74
|
+
| `name` | Unique service name — Secrets Manager / ECS key strips a leading `ossy-` (`website-ossy`). |
|
|
75
|
+
| `domain` | Primary hostname (default ALB host when `hosts` is omitted). |
|
|
76
|
+
| `hosts` | Optional ALB host-header list. Example: `["ossy.se","api.ossy.se"]` so the website image serves apex and API host. Defaults to `[domain]`. |
|
|
77
|
+
| `image` | Docker image to pull and run (GHCR). |
|
|
84
78
|
|
|
85
|
-
|
|
79
|
+
**Built-in (always present):**
|
|
80
|
+
- `runtime` — platform runtime (`ghcr.io/ossy-se/platform:latest`) serving CMS-published apps; ALB default + unclaimed `domains`.
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
**Not deployed:** separate `ossy-api` container; TCP/UDP `services[]` entries (ignored by the ECS path).
|
|
88
83
|
|
|
89
|
-
|
|
84
|
+
**Wildcard subdomains:** Add `*.ossy.se` to `domains` for a Route53 wildcard alias to CloudFront.
|
|
90
85
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
`MEDIA_REPOSITORY` and `MEDIA_CDN_DOMAIN_NAME` are derived from the provisioned S3 bucket and media CloudFront — do not set them in `env`.
|
|
87
|
+
|
|
88
|
+
To add a new platform, add an entry to `platforms.json`. No bucket name or CDN domain is needed; these are auto-generated by CloudFormation.
|
|
89
|
+
|
|
90
|
+
## Platform secrets (Secrets Manager)
|
|
91
|
+
|
|
92
|
+
Each platform service gets **one** Secrets Manager secret (JSON object of env keys). Values still live in `platforms.json` for now; CDK owns the secret resources and IAM, and deploy upserts values.
|
|
93
|
+
|
|
94
|
+
| Secret name | Source |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `{platform}/runtime` | Built-in platform-runtime |
|
|
97
|
+
| `{platform}/website-ossy` | HTTP entry in `services[]` named `ossy-website-ossy` (leading `ossy-` stripped) |
|
|
98
|
+
| `{platform}/…` | Other HTTP `services[]` entries |
|
|
99
|
+
|
|
100
|
+
**Not created:** `ossy-api`. TCP/UDP `services[]` entries are skipped.
|
|
101
|
+
|
|
102
|
+
Each secret has a matching IAM role `{platform}-{key}-task` assumed by `ecs-tasks.amazonaws.com` with read access to that secret only.
|
|
103
|
+
|
|
104
|
+
Also created: `{platform}/ghcr-pull` — Docker Hub-style credentials JSON (`username` + `password` PAT with `read:packages`) for private GHCR pulls. **Not** filled by `sync-secrets`; set manually once after the secrets stack deploys.
|
|
105
|
+
|
|
106
|
+
### Sync values after CDK deploy
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cd packages/deployment-tools
|
|
110
|
+
|
|
111
|
+
# Create / update secret resources + IAM (part of the stage)
|
|
112
|
+
npx cdk deploy 'ossybot/platform-secrets' --profile ossybot
|
|
113
|
+
|
|
114
|
+
# Upsert secret *values* from platforms.json (never prints secret contents)
|
|
115
|
+
npm run sync-secrets -- --profile ossybot
|
|
116
|
+
|
|
117
|
+
# Optional: one platform, or dry-run
|
|
118
|
+
npm run sync-secrets -- --profile ossybot --platform ossybot
|
|
119
|
+
npm run sync-secrets -- --dry-run
|
|
101
120
|
```
|
|
102
121
|
|
|
103
|
-
|
|
122
|
+
Do **not** remove secrets from `platforms.json` yet.
|
|
123
|
+
|
|
124
|
+
## ECS Fargate + ALB
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
cd packages/deployment-tools
|
|
128
|
+
|
|
129
|
+
# Secrets + IAM first, then sync values + set ghcr-pull
|
|
130
|
+
npx cdk deploy 'ossybot/platform-secrets' --profile ossybot
|
|
131
|
+
npm run sync-secrets -- --profile ossybot
|
|
132
|
+
# then put GHCR credentials into ossybot/ghcr-pull
|
|
133
|
+
|
|
134
|
+
# VPC, cluster, Fargate services, ALB host rules
|
|
135
|
+
npx cdk deploy 'ossybot/platform-ecs' --profile ossybot
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Resource | Notes |
|
|
139
|
+
|---|---|
|
|
140
|
+
| VPC | Public + private subnets, 1 NAT gateway |
|
|
141
|
+
| ECS cluster | `{platform}-platform` |
|
|
142
|
+
| Fargate services | `runtime` (`ghcr.io/ossy-se/platform:latest`) + each HTTP `services[]` entry |
|
|
143
|
+
| ALB | Internet-facing HTTP :80; host rules; default → runtime; health check `/health` |
|
|
144
|
+
| Task env | Secrets Manager JSON keys from `platforms.json` `env`; `MEDIA_*` from storage stack |
|
|
145
|
+
|
|
146
|
+
## CloudFront + WAF + ACM
|
|
147
|
+
|
|
148
|
+
App traffic uses its own CloudFront distribution (do **not** overload the `/media` CDN). The edge stack is deployed in **`us-east-1`** because CloudFront viewer certificates and `CLOUDFRONT`-scope WAFv2 web ACLs must live there.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# After platform-ecs exists (ALB DNS used as origin)
|
|
152
|
+
npx cdk deploy 'ossybot/platform-edge' --profile ossybot
|
|
153
|
+
|
|
154
|
+
# DNS aliases for known domains → CloudFront
|
|
155
|
+
npx cdk deploy 'ossybot/dns' --profile ossybot
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
| Piece | Detail |
|
|
104
159
|
|---|---|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
160
|
+
| Origin | ALB DNS name from `platform-ecs` (`LoadBalancerDnsName`), HTTP :80 |
|
|
161
|
+
| Certificate | ACM in `us-east-1`, DNS-validated; covers `domains` + HTTP service hosts (apex + `*.ossy.se` etc.) |
|
|
162
|
+
| WAF | WAFv2 web ACL (`AWSManagedRulesCommonRuleSet` + `KnownBadInputs`) associated via `webAclId` |
|
|
163
|
+
| Cache | `CACHING_DISABLED` + `ALL_VIEWER` origin request policy (forwards `Host`) |
|
|
164
|
+
| DNS | Route53 alias A/AAAA for known domains → CloudFront |
|
|
110
165
|
|
|
111
|
-
|
|
112
|
-
- `ossy-api` — the Ossy API (`ghcr.io/ossy-se/api:latest`, port 3001)
|
|
113
|
-
- `ossy-runtime` — the platform runtime serving all CMS-published apps (`ghcr.io/ossy-se/platform:latest`, port 3000, catch-all)
|
|
166
|
+
## Release to ECS from GitHub Actions
|
|
114
167
|
|
|
115
|
-
**
|
|
168
|
+
Images stay on **GHCR**. After a push to `main`, Publish workflows tag the image with the git SHA (and `:latest`), assume the platform deploy role via **GitHub OIDC** (no long-lived AWS keys), register a new task definition revision with the SHA tag, and update the ECS service.
|
|
116
169
|
|
|
117
|
-
|
|
170
|
+
```bash
|
|
171
|
+
# After platform-ecs exists — creates OIDC provider + deploy role
|
|
172
|
+
npx cdk deploy 'ossybot/platform-ci' --profile ossybot
|
|
173
|
+
```
|
|
118
174
|
|
|
119
|
-
|
|
175
|
+
| Piece | Detail |
|
|
176
|
+
|---|---|
|
|
177
|
+
| Role | `{platform}-github-deploy` (e.g. `ossybot-github-deploy`) |
|
|
178
|
+
| Trust | OIDC `token.actions.githubusercontent.com`, `sub` = `repo:<owner>/<repo>:ref:refs/heads/main` for each entry in `platforms.json` → `githubDeployRepos` |
|
|
179
|
+
| Cluster / services | `{platform}-platform` / `{platform}-{serviceKey}` (e.g. `ossybot-runtime`, `ossybot-website-ossy`) |
|
|
180
|
+
| Image tags | `ghcr.io/ossy-se/<image>:latest` and `:<git-sha>` |
|
|
181
|
+
| Workflows | each trusted repo’s Publish workflow rolls its ECS service |
|
|
120
182
|
|
|
121
|
-
|
|
183
|
+
Add another deploy source by appending `owner/repo` to `githubDeployRepos` and redeploying `platform-ci`. Deploy that stack once before the first Actions rollout. Workflows wait for ECS service stability after update.
|
|
122
184
|
|
|
123
185
|
## CDK Stacks
|
|
124
186
|
|
|
125
|
-
Each platform is a CDK `Stage` containing
|
|
187
|
+
Each platform is a CDK `Stage` containing these stacks:
|
|
126
188
|
|
|
127
189
|
| Stack | Description |
|
|
128
190
|
|---|---|
|
|
129
|
-
| `storage-static` | S3 bucket (auto-named), daily backup plan, CloudFront CDN for `/media`.
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
191
|
+
| `storage-static` | S3 bucket (auto-named), daily backup plan, CloudFront CDN for `/media`. |
|
|
192
|
+
| `platform-secrets` | Secrets Manager secrets per service + ECS task roles (read IAM) + `ghcr-pull`. Values synced from `platforms.json` via `npm run sync-secrets`. |
|
|
193
|
+
| `platform-ecs` | VPC, ECS Fargate services, ALB with host-based routing. |
|
|
194
|
+
| `platform-ci` | GitHub OIDC provider + `{platform}-github-deploy` IAM role for ECS rollouts from Actions. |
|
|
195
|
+
| `platform-edge` | CloudFront + WAF + ACM in `us-east-1` in front of the ALB. |
|
|
196
|
+
| `dns` | Route53 alias records for known domains → CloudFront app distribution. |
|
|
132
197
|
| `ses` | SES email sending identity and DKIM records. |
|
|
133
198
|
|
|
134
|
-
|
|
199
|
+
### Cutover / decommission EC2 (#560)
|
|
200
|
+
|
|
201
|
+
CDK no longer synthesizes `deployment-target` (EC2 + EIP + Caddy + systemd units). After `platform-edge` and `dns` are live and traffic is healthy:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
# Destroy the leftover CloudFormation stack (name may be stage-prefixed)
|
|
205
|
+
npx cdk destroy 'ossybot/deployment-target' --profile ossybot
|
|
206
|
+
# or, if the stack still exists outside the app:
|
|
207
|
+
# aws cloudformation delete-stack --stack-name ossybot-deployment-target --profile ossybot
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Downtime during cutover is acceptable. Confirm known domains resolve to CloudFront before destroying EC2.
|
|
135
211
|
|
|
136
212
|
## Bootstrapping a new platform
|
|
137
213
|
|
|
138
214
|
```bash
|
|
139
215
|
cd packages/deployment-tools
|
|
140
216
|
npx cdk deploy 'ossybot/**' --profile ossybot --require-approval never
|
|
217
|
+
npm run sync-secrets -- --profile ossybot
|
|
218
|
+
# set {platform}/ghcr-pull credentials once
|
|
141
219
|
```
|
|
142
220
|
|
|
143
|
-
CDK provisions the instance, writes all service files, writes `/etc/environment` from `platforms.json`, and starts all services automatically. No manual SSH steps are required.
|
|
144
|
-
|
|
145
|
-
> Note: EC2 user data only runs on **first boot**. To apply infrastructure changes to an existing instance you must terminate and recreate it, or SSH in and apply changes manually.
|
|
146
|
-
|
|
147
221
|
## Deploying changes
|
|
148
222
|
|
|
149
223
|
Target a single platform:
|
|
@@ -168,32 +242,18 @@ npx cdk list
|
|
|
168
242
|
|
|
169
243
|
## Redeploying after a code change
|
|
170
244
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
# API — push to main triggers CI → ghcr.io/ossy-se/api:latest
|
|
175
|
-
sudo systemctl restart ossy-api
|
|
176
|
-
|
|
177
|
-
# Platform runtime — merge to main in ossy monorepo → ghcr.io/ossy-se/platform:latest
|
|
178
|
-
sudo systemctl restart ossy-runtime
|
|
179
|
-
|
|
180
|
-
# website-ossy — push to main in website-ossy repo → ghcr.io/ossy-se/website-ossy:latest
|
|
181
|
-
sudo systemctl restart ossy-website-ossy
|
|
182
|
-
|
|
183
|
-
# website-plexus-sanitas — push to main → ghcr.io/ossy-se/website-plexus-sanitas:latest
|
|
184
|
-
sudo systemctl restart ossy-website-plexus-sanitas
|
|
185
|
-
```
|
|
245
|
+
Merge to `main` — Publish workflows push GHCR tags (`:latest` + `:<sha>`) and roll the matching ECS service. No SSH / `systemctl`.
|
|
186
246
|
|
|
187
247
|
## Environment variables
|
|
188
248
|
|
|
189
|
-
Defined in the `env` block of `platforms.json`,
|
|
249
|
+
Defined in the `env` block of `platforms.json`, synced into Secrets Manager. The following are injected automatically by the ECS stack and should **not** be set manually:
|
|
190
250
|
|
|
191
251
|
| Variable | Source |
|
|
192
252
|
|---|---|
|
|
193
253
|
| `MEDIA_REPOSITORY` | S3 bucket name from `storage-static` stack |
|
|
194
254
|
| `MEDIA_CDN_DOMAIN_NAME` | CloudFront domain from `storage-static` stack |
|
|
195
255
|
|
|
196
|
-
|
|
256
|
+
Other common env vars:
|
|
197
257
|
|
|
198
258
|
| Variable | Used by | Description |
|
|
199
259
|
|---|---|---|
|
|
@@ -209,32 +269,10 @@ All other env vars:
|
|
|
209
269
|
|
|
210
270
|
## Useful commands
|
|
211
271
|
|
|
212
|
-
**SSH into instance**
|
|
213
|
-
```bash
|
|
214
|
-
ssh -i path/to/keys ubuntu@<elastic-ip>
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
**View service logs**
|
|
218
|
-
```bash
|
|
219
|
-
journalctl -u ossy-runtime.service -n 200 -f
|
|
220
|
-
journalctl -u ossy-api.service -n 200 -f
|
|
221
|
-
journalctl -u ossy-website-ossy.service -n 200 -f
|
|
222
|
-
journalctl -u caddy-route53.service -n 200 -f
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
**View running containers**
|
|
226
272
|
```bash
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
docker logs ossy-runtime
|
|
233
|
-
docker logs ossy-api
|
|
234
|
-
docker logs ossy-website-ossy
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Reload Caddy config**
|
|
238
|
-
```bash
|
|
239
|
-
sudo systemctl reload caddy-route53.service
|
|
273
|
+
cd packages/deployment-tools
|
|
274
|
+
npm test
|
|
275
|
+
npx cdk list
|
|
276
|
+
npx cdk diff 'ossybot/**' --profile ossybot
|
|
277
|
+
npm run sync-secrets -- --profile ossybot --dry-run
|
|
240
278
|
```
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ossy/deployment-tools",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Collection of scripts and tools to aid deployment of containers and static files to Amazon Web Services through GitHub Actions",
|
|
5
5
|
"source": "./src/index.js",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "
|
|
8
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
9
9
|
"build": "echo \"The build step is not required when using JavaScript!\" && exit 0",
|
|
10
10
|
"build:docs": "jsdoc -c jsdoc.config.js",
|
|
11
|
-
"cdk": "cdk"
|
|
11
|
+
"cdk": "cdk",
|
|
12
|
+
"sync-secrets": "node ./src/secrets/sync-platform-secrets.js"
|
|
12
13
|
},
|
|
13
14
|
"author": "Ossy",
|
|
14
15
|
"license": "ISC",
|
|
15
16
|
"dependencies": {
|
|
17
|
+
"@aws-sdk/client-secrets-manager": "^3.0.0",
|
|
16
18
|
"aws-cdk": "^2.73.0",
|
|
17
19
|
"aws-cdk-lib": "^2.73.0",
|
|
18
20
|
"constructs": "^10.1.304",
|
|
@@ -22,5 +24,5 @@
|
|
|
22
24
|
"devDependencies": {
|
|
23
25
|
"jest": "^30.4.2"
|
|
24
26
|
},
|
|
25
|
-
"gitHead": "
|
|
27
|
+
"gitHead": "d36b69444268d172bc3e8e1dc77e67afc63f8650"
|
|
26
28
|
}
|
|
@@ -3,31 +3,31 @@
|
|
|
3
3
|
* @typedef {Object} PlatformConfig
|
|
4
4
|
* @property {string} platformName - Name of platform
|
|
5
5
|
* @property {string[]=} sesDomains - list of domains to configure AWS SES email identity and DKIM records for
|
|
6
|
-
* @property {string[]=} domains - list of domains
|
|
6
|
+
* @property {string[]=} domains - list of domains for Route53 aliases (CloudFront app edge) and ACM coverage
|
|
7
7
|
* @property {object} dnsRecords - map of dns records by root domain, only supports MX records so that we can add dns records for our email service
|
|
8
|
-
* @property {Array<HttpServiceConfig|TcpServiceConfig>=} services - per-platform container services
|
|
8
|
+
* @property {Array<HttpServiceConfig|TcpServiceConfig>=} services - per-platform container services. HTTP entries become ECS Fargate services + ALB host rules. TCP/UDP entries are ignored by the ECS path (not deployed).
|
|
9
|
+
* @property {Record<string,string>=} env - environment variables upserted into per-service Secrets Manager secrets via `npm run sync-secrets`. Never uploaded to S3. Values stay in git for now (#555).
|
|
10
|
+
* @property {string} awsAccountId - Aws account id
|
|
11
|
+
* @property {string=} awsRegion - AWS region, defaults to eu-north-1
|
|
12
|
+
* @property {string=} awsKeyPairName - unused after EC2 decommission (#560); ignored by CDK
|
|
13
|
+
* @property {string=} awsInstanceClass - unused after EC2 decommission (#560); ignored by CDK
|
|
14
|
+
* @property {string=} awsInstanceSize - unused after EC2 decommission (#560); ignored by CDK
|
|
15
|
+
* @property {string[]=} githubDeployRepos - `owner/repo` list trusted by the GitHub OIDC deploy role (#559); required for platform-ci, set in platforms.json
|
|
16
|
+
* @property {string=} ciDockerNetworkName - ?
|
|
9
17
|
*
|
|
10
18
|
* @typedef {Object} HttpServiceConfig
|
|
11
19
|
* @property {'http'} [type] - defaults to 'http' when omitted
|
|
12
|
-
* @property {string} name - unique service name (
|
|
13
|
-
* @property {string} domain - hostname
|
|
20
|
+
* @property {string} name - unique service name (ECS service key derived by stripping leading `ossy-`)
|
|
21
|
+
* @property {string} domain - primary hostname (default ALB host when `hosts` omitted)
|
|
22
|
+
* @property {string[]=} hosts - ALB host-header values (#557); defaults to `[domain]`
|
|
14
23
|
* @property {string} image - Docker image (e.g. ghcr.io/ossy-se/website-ossy:latest)
|
|
15
24
|
*
|
|
16
25
|
* @typedef {Object} TcpServiceConfig
|
|
17
|
-
* @property {'tcp'} type -
|
|
18
|
-
* @property {string} name - unique service name
|
|
26
|
+
* @property {'tcp'} type - raw socket service; not deployed on the ECS path (#560)
|
|
27
|
+
* @property {string} name - unique service name
|
|
19
28
|
* @property {string} image - Docker image (e.g. itzg/minecraft-server)
|
|
20
|
-
* @property {number[]} ports - ports
|
|
29
|
+
* @property {number[]} ports - ports (unused by current CDK stacks)
|
|
21
30
|
* @property {'tcp'|'udp'|'both'} [protocol] - defaults to 'tcp'
|
|
22
|
-
* @property {Record<string,string>=} env - environment variables written to /etc/environment on the EC2 host at boot time; never uploaded to S3
|
|
23
|
-
*
|
|
24
|
-
* @property {string} awsAccountId - Aws account id
|
|
25
|
-
* @property {string=} awsRegion - AWS region, defaults to eu-north-1
|
|
26
|
-
* @property {string=} awsKeyPairName - EC2 key pair name for SSH access
|
|
27
|
-
* @property {string=} awsInstanceClass - EC2 instance class (e.g. t3, t4g, m5), defaults to t3
|
|
28
|
-
* @property {string=} awsInstanceSize - EC2 instance size (e.g. small, medium, large), defaults to small
|
|
29
|
-
*
|
|
30
|
-
* @property {string=} ciDockerNetworkName - ?
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
const SupportedRegions = {
|