@fabrika/provider-zerops 0.4.0 → 0.4.2
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/package.json +7 -7
- package/src/installation/guide.md +207 -0
- package/src/installation/index.ts +20 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabrika/provider-zerops",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"zod": "^3.25.76",
|
|
46
|
-
"@fabrika/auth": "^0.4.
|
|
47
|
-
"@fabrika/auth-core": "^0.4.
|
|
48
|
-
"@fabrika/iam-contract": "^0.4.
|
|
49
|
-
"@fabrika/installation-init": "^0.4.
|
|
50
|
-
"@fabrika/provider-contract": "^0.4.
|
|
51
|
-
"@fabrika/proxy-core": "^0.4.
|
|
46
|
+
"@fabrika/auth": "^0.4.2",
|
|
47
|
+
"@fabrika/auth-core": "^0.4.2",
|
|
48
|
+
"@fabrika/iam-contract": "^0.4.2",
|
|
49
|
+
"@fabrika/installation-init": "^0.4.2",
|
|
50
|
+
"@fabrika/provider-contract": "^0.4.2",
|
|
51
|
+
"@fabrika/proxy-core": "^0.4.2",
|
|
52
52
|
"yaml": "^2.9.1"
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Installing fabrika on Zerops — the operator's guide
|
|
2
|
+
|
|
3
|
+
From an empty Zerops account to a serving application. This guide ships with the CLI, so it matches the
|
|
4
|
+
release you run. It gives the order and the traps. Each command's flags are in its own help, and that text
|
|
5
|
+
is authoritative:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
fabrika platform --provider=zerops --help
|
|
9
|
+
fabrika control --help
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Run the steps in order and check each one before the next. Every command refuses a missing value with a
|
|
13
|
+
message that names the flag or the variable to set. Read the message: it names the fix.
|
|
14
|
+
|
|
15
|
+
## Before you start
|
|
16
|
+
|
|
17
|
+
**Ask the human for these first.** You cannot produce them, and each one blocks a later step:
|
|
18
|
+
|
|
19
|
+
| What | Needed at |
|
|
20
|
+
| ------------------------------------------------------------------------------ | ---------- |
|
|
21
|
+
| A Zerops personal access token, generated in the Zerops GUI | step 1 |
|
|
22
|
+
| A `gh` login allowed to create a repository in the organization that owns it | step 3 |
|
|
23
|
+
| The administrator's e-mail, and the human to open the enrollment URL | step 2 |
|
|
24
|
+
| For a PRIVATE application repository: the GitHub App connected in the console | step 5 |
|
|
25
|
+
| The fabrika release tag to install, for example `v0.4.2` (a branch is refused) | steps 1, 3 |
|
|
26
|
+
|
|
27
|
+
Also needed on the machine: [Bun](https://bun.sh) ≥ 1.3, `git`, `gh`, `curl` and `jq`.
|
|
28
|
+
|
|
29
|
+
## Secrets: rules for an agent
|
|
30
|
+
|
|
31
|
+
- Every credential is read from the ENVIRONMENT ONLY. No command takes one as a flag.
|
|
32
|
+
- Never print a credential, never put one in a command line you echo, never commit one.
|
|
33
|
+
- Keep each one in its own file with mode `0600` (`umask 077` first) and pass it as
|
|
34
|
+
`NAME="$(cat <file>)" fabrika …`. Check a file with `wc -c`, never with `cat`.
|
|
35
|
+
- The provisioning key from step 1 is printed ONCE. Capture it from the output into its file at once.
|
|
36
|
+
The only other copies are in the `iam` and `control` service environments.
|
|
37
|
+
|
|
38
|
+
## Step 0. Get the CLI at the release tag
|
|
39
|
+
|
|
40
|
+
`platform install` and `platform deploy` upload the source of the checkout the CLI runs from, and refuse
|
|
41
|
+
anything else (`--source-dir`). So the bring-up runs from a clean clone at the tag, not from a CLI installed
|
|
42
|
+
with `bun add --global`:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone --depth 1 --branch <tag> https://github.com/contember/fabrika-platform.git fabrika-platform
|
|
46
|
+
cd fabrika-platform && bun install --frozen-lockfile
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
In this guide, `fabrika` means `bun <clone>/packages/cli/src/index.ts`. Do not edit the clone: a dirty
|
|
50
|
+
checkout is refused, and a local commit is not on GitHub, so namespaces cannot fetch it.
|
|
51
|
+
|
|
52
|
+
## Step 1. Install the platform
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
export FABRIKA_ZEROPS_ACCESS_TOKEN="$(cat <personal token file>)"
|
|
56
|
+
curl -s -H "Authorization: Bearer $FABRIKA_ZEROPS_ACCESS_TOKEN" \
|
|
57
|
+
https://api.app-prg1.zerops.io/api/rest/public/user/info | jq '.clientUserList[] | {clientId, name: .client.accountName}'
|
|
58
|
+
fabrika platform install --provider=zerops --create-project --client-id=<clientId> --env=<name> --source-dir=. --yes > install.log 2>&1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Run it from the clone root. It runs for many minutes: it imports six services, builds the proxy once to
|
|
62
|
+
learn the public hosts, then deploys everything.
|
|
63
|
+
- `--yes` is required when stdin is not a terminal.
|
|
64
|
+
- Capture the key right away and keep the log away from anything that publishes it:
|
|
65
|
+
`grep -o 'FABRIKA_IAM_PROVISIONING_KEY=px_[A-Za-z0-9_-]*' install.log | cut -d= -f2 > provisioning.key`
|
|
66
|
+
- The project id is printed BEFORE the long wait. If the run is interrupted, re-run with
|
|
67
|
+
`--project-id=<id>` instead of `--create-project`.
|
|
68
|
+
- A project that already holds generated secrets is refused. Do not try to force it: a second bring-up
|
|
69
|
+
over the same project would destroy the vault key. Start over in a new empty project.
|
|
70
|
+
- The account must be in the default region, or set `FABRIKA_ZEROPS_API_URL` for every command.
|
|
71
|
+
|
|
72
|
+
**Check.** The log ends with `✓ iam <host>`, the console origin and the provisioning key box. Write down
|
|
73
|
+
the three hosts: IAM, console and Operations.
|
|
74
|
+
|
|
75
|
+
## Step 2. Admit the first administrator
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
FABRIKA_IAM_PROVISIONING_KEY="$(cat provisioning.key)" \
|
|
79
|
+
fabrika platform admin --provider=zerops --email=<address> --iam-host=<IAM host from step 1>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
It prints one enrollment URL. The URL is a credential: give it to the human only, and do not log it. The
|
|
83
|
+
human opens it, sets a password and signs in at the console origin. A re-run creates nothing new;
|
|
84
|
+
`--reissue` replaces an enrollment that expired unopened.
|
|
85
|
+
|
|
86
|
+
## Step 3. Create the sidecar repository
|
|
87
|
+
|
|
88
|
+
The sidecar is a GitHub repository the operator owns. Its workflow runs `platform deploy` at the pinned tag.
|
|
89
|
+
|
|
90
|
+
Read the integration token that step 1 minted. Never use the personal token here:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
A=https://api.app-prg1.zerops.io/api/rest/public
|
|
94
|
+
H="Authorization: Bearer $FABRIKA_ZEROPS_ACCESS_TOKEN"
|
|
95
|
+
control=$(curl -s -H "$H" "$A/project/<project id>/service-stack?limit=100&offset=0" | jq -r '.list[] | select(.name=="control") | .id')
|
|
96
|
+
curl -s -H "$H" "$A/service-stack/$control/env" | jq -r '.items[] | select(.key=="FABRIKA_ZEROPS_ACCESS_TOKEN") | .content' > integration.token
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then run `init` from a directory that is NOT inside any git repository. The fabrika clone is a repository,
|
|
100
|
+
and a home directory under dotfiles version control is one too. Otherwise pass `--checkout=<dir>` outside one:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
FABRIKA_ZEROPS_ACCESS_TOKEN="$(cat integration.token)" FABRIKA_IAM_PROVISIONING_KEY="$(cat provisioning.key)" \
|
|
104
|
+
fabrika platform init --provider=zerops <installation> --repo=<owner>/<name> --tag=<tag> \
|
|
105
|
+
--project-id=<project id> --env=<name from step 1> --yes < /dev/null
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- Without `--repo` the repository is `contember/fabrika-zerops-<installation>`. Pass `--repo` for any
|
|
109
|
+
other organization.
|
|
110
|
+
- Keep the local checkout path. `platform upgrade` needs it later (`--sidecar=<dir>`).
|
|
111
|
+
|
|
112
|
+
**Check.** The `platform` workflow run in the sidecar repository ends green:
|
|
113
|
+
`gh run list --repo <owner>/<name>`.
|
|
114
|
+
|
|
115
|
+
## Step 4. Mint a machine key for `fabrika control`
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
iam=$(curl -s -H "$H" "$A/project/<project id>/service-stack?limit=100&offset=0" | jq -r '.list[] | select(.name=="iam") | .id')
|
|
119
|
+
curl -s -H "$H" "$A/service-stack/$iam/env" | jq -r '.items[] | select(.key=="FABRIKA_IAM_RPC_KEY") | .content' > iam-rpc.key
|
|
120
|
+
FABRIKA_IAM_RPC_URL=https://<IAM host> FABRIKA_IAM_RPC_KEY="$(cat iam-rpc.key)" FABRIKA_IAM_PROVISIONING_KEY="$(cat provisioning.key)" \
|
|
121
|
+
fabrika control key issue --label=<name> --permissions=app.manage,namespace.manage,deploy.trigger,deploy.read \
|
|
122
|
+
--expires-in=2592000 > control-key.out
|
|
123
|
+
grep -o 'px_[A-Za-z0-9_-]*' control-key.out | head -1 > control.key && rm control-key.out
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `FABRIKA_IAM_RPC_URL` is IAM's PUBLIC `https://` origin. The private `http://iam:3000` answers only inside
|
|
127
|
+
the Zerops project.
|
|
128
|
+
- A key expires after at most 30 days. Mint a new one when it does.
|
|
129
|
+
|
|
130
|
+
For every `fabrika control` command from here on:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
export FABRIKA_CONTROL_URL=https://<console host>
|
|
134
|
+
FABRIKA_CONTROL_KEY="$(cat control.key)" fabrika control apps list
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Check.** `apps list` answers, with an empty list on a fresh installation.
|
|
138
|
+
|
|
139
|
+
## Step 5. Deploy the first application
|
|
140
|
+
|
|
141
|
+
The public example is [`contember/fabrika-example-zerops`](https://github.com/contember/fabrika-example-zerops).
|
|
142
|
+
Its `fabrika.config.ts` places the app with its own database (`mid` preset), and `fabrika.cheap.config.ts`
|
|
143
|
+
places it on the namespace's shared PostgreSQL (`cheap` preset). Use the config that matches the preset.
|
|
144
|
+
|
|
145
|
+
1. **Create a namespace.** It is the Zerops project applications are placed in. Without a custom domain
|
|
146
|
+
bound to its proxy, pass `--public-access=zerops-subdomain`; the default is `custom-domain`, which serves
|
|
147
|
+
no host until you bind one.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
fabrika control namespaces plan --namespace=<id> --env=<name> --preset=mid --public-access=zerops-subdomain
|
|
151
|
+
fabrika control namespaces create --namespace=<id> --env=<name> --preset=mid --public-access=zerops-subdomain
|
|
152
|
+
fabrika control namespaces get --namespace=<id>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`create` returns while the state is `pending`. Provisioning takes several minutes. Poll `get` every
|
|
156
|
+
30 seconds until it is `ready`, then pick a host it lists as `free`.
|
|
157
|
+
|
|
158
|
+
2. **Build and register**, from the application's repository root. The repository must be pushed: control
|
|
159
|
+
deploys the commit on GitHub, not your working tree.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
bun install
|
|
163
|
+
bunx fabrika app build --env=<name> # cheap: add --config=fabrika.cheap.config.ts
|
|
164
|
+
FABRIKA_CONTROL_KEY="$(cat control.key)" bunx fabrika control register --provider=zerops --app=<id> \
|
|
165
|
+
--repo=https://github.com/<owner>/<name> --env=<name> --manifest=fabrika.manifest.json \
|
|
166
|
+
--namespace=<namespace id> --domain=<free host> --installation-id=none
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- `--installation-id=none` is for a PUBLIC repository. A private one needs the GitHub App connected in
|
|
170
|
+
the console (Settings → Source, a browser flow for the human); then omit the flag.
|
|
171
|
+
- `register` CREATES and refuses a second registration. A changed manifest goes through
|
|
172
|
+
`control apps environments put` with the same placement flags.
|
|
173
|
+
|
|
174
|
+
3. **Deploy and follow it.**
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
FABRIKA_CONTROL_KEY="$(cat control.key)" bunx fabrika control deploy --app=<id> --env=<name>
|
|
178
|
+
FABRIKA_CONTROL_KEY="$(cat control.key)" bunx fabrika control runs list --app=<id>
|
|
179
|
+
FABRIKA_CONTROL_KEY="$(cat control.key)" bunx fabrika control runs log --run=<run id>
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The deploy fails when the repository at the deployed commit does not match the registered manifest.
|
|
183
|
+
Commit and push, rebuild the manifest, `apps environments put` it, and deploy again.
|
|
184
|
+
|
|
185
|
+
On an environment named `prod`, a change that widens access waits for a person to approve it in the
|
|
186
|
+
console before it deploys.
|
|
187
|
+
|
|
188
|
+
**Check.** The run ends `succeeded`. `https://<host>/healthz` answers 200, and `/` redirects to the IAM
|
|
189
|
+
sign-in. A green run proves only that Zerops accepted the build: have the human sign in and use the app.
|
|
190
|
+
|
|
191
|
+
## Step 6. Upgrade later
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
fabrika platform upgrade --provider=zerops --to=<newer tag> --sidecar=<sidecar checkout>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
It commits the tag to the sidecar's `fabrika.ref`, pushes, and follows the workflow run it triggers. It
|
|
198
|
+
needs only the `gh` login. Existing namespaces keep their proxy version.
|
|
199
|
+
|
|
200
|
+
## When something fails
|
|
201
|
+
|
|
202
|
+
- Every refusal names its flag or variable. Fix that one value and re-run. `init`, `admin`, `deploy` and
|
|
203
|
+
`upgrade` are safe to re-run. `install` is not: see step 1.
|
|
204
|
+
- A Zerops build or deploy failure: read the service log in the Zerops GUI or with
|
|
205
|
+
`zcli service log -S <service id> --show-build-logs`.
|
|
206
|
+
- Do not work around a refusal by editing the clone or the generated sidecar. Report what failed, with the
|
|
207
|
+
command and its message, to the people who maintain fabrika.
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '@fabrika/installation-init'
|
|
10
10
|
import type { SchemaReconciler } from '@fabrika/provider-contract'
|
|
11
11
|
import type { InstallationCli, InstallationCommand } from '@fabrika/provider-contract/installation'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
12
13
|
import { createZeropsApi, defaultSleep } from '../index'
|
|
13
14
|
import { consoleAdminCollaborators, runPlatformAdmin } from './admin'
|
|
14
15
|
import { parsePlatformAdminArgs, REISSUE_FLAG } from './admin-options'
|
|
@@ -44,7 +45,11 @@ export {
|
|
|
44
45
|
|
|
45
46
|
const USAGE = `Zerops installation
|
|
46
47
|
|
|
48
|
+
New here, or an agent? Run \`fabrika platform guide --provider=zerops\` first: the whole bring-up in order,
|
|
49
|
+
what to ask the human for, and the traps this text cannot show.
|
|
50
|
+
|
|
47
51
|
Commands:
|
|
52
|
+
fabrika platform guide --provider=zerops
|
|
48
53
|
fabrika platform install --provider=zerops [options]
|
|
49
54
|
fabrika platform init --provider=zerops <installation> --tag=<tag> --project-id=<id> [options]
|
|
50
55
|
fabrika platform plan --provider=zerops
|
|
@@ -305,6 +310,16 @@ laptop and not in the pipeline it triggers.
|
|
|
305
310
|
/** No cancellation source exists behind a CLI invocation; the ports all take a signal regardless. */
|
|
306
311
|
const neverAborted = (): AbortSignal => new AbortController().signal
|
|
307
312
|
|
|
313
|
+
/** The operator's guide of this release, shipped beside this module. */
|
|
314
|
+
export const readInstallationGuide = (): Promise<string> => Bun.file(fileURLToPath(new URL('./guide.md', import.meta.url))).text()
|
|
315
|
+
|
|
316
|
+
const runGuide = async (argv: readonly string[]): Promise<void> => {
|
|
317
|
+
if (argv.length > 0) {
|
|
318
|
+
throw new Error(`Unexpected Zerops guide arguments: ${argv.map(describeArgument).join(' ')}`)
|
|
319
|
+
}
|
|
320
|
+
console.info(await readInstallationGuide())
|
|
321
|
+
}
|
|
322
|
+
|
|
308
323
|
const runPlan = (argv: readonly string[]): void => {
|
|
309
324
|
if (argv.length > 0) {
|
|
310
325
|
throw new Error(`Unexpected Zerops plan arguments: ${argv.map(describeArgument).join(' ')}`)
|
|
@@ -368,9 +383,13 @@ const runPlatformUpgrade = async (argv: readonly string[]): Promise<void> => {
|
|
|
368
383
|
|
|
369
384
|
export const installationCli: InstallationCli = {
|
|
370
385
|
provider: 'zerops',
|
|
371
|
-
commands: ['install', 'init', 'plan', 'deploy', 'admin', 'upgrade'],
|
|
386
|
+
commands: ['install', 'init', 'plan', 'deploy', 'admin', 'upgrade', 'guide'],
|
|
372
387
|
usage: USAGE,
|
|
373
388
|
run: async (command: InstallationCommand, argv: readonly string[]) => {
|
|
389
|
+
if (command === 'guide') {
|
|
390
|
+
await runGuide(argv)
|
|
391
|
+
return
|
|
392
|
+
}
|
|
374
393
|
if (command === 'install') {
|
|
375
394
|
await runPlatformInstall(argv)
|
|
376
395
|
return
|