unoverse 0.1.82 → 0.1.84
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.
|
@@ -80,6 +80,37 @@
|
|
|
80
80
|
See docs/runbooks/02-database.md for details.
|
|
81
81
|
when: ext_result.rc != 0
|
|
82
82
|
|
|
83
|
+
# BEFORE MIGRATIONS, OR THEY CANNOT CREATE THEIR OWN BOOKKEEPING TABLE. PostgreSQL 15+
|
|
84
|
+
# dropped the implicit CREATE on schema public, and DigitalOcean creates every database
|
|
85
|
+
# owned by its admin user, so the universe user connects fine and then fails on the
|
|
86
|
+
# first CREATE TABLE. Granting is idempotent and only ever widens this one user's rights
|
|
87
|
+
# inside its own database — it touches nothing else on a shared cluster.
|
|
88
|
+
#
|
|
89
|
+
# no_log because the admin connection string is an argument here. It arrives from
|
|
90
|
+
# terraform at deploy time and is never written to the server.
|
|
91
|
+
- name: "[3b/5] Grant the universe user rights on its own schema"
|
|
92
|
+
command: >
|
|
93
|
+
docker compose exec -T -e NODE_TLS_REJECT_UNAUTHORIZED=0 -e ADMIN_URL={{ pg_admin_url }} unoverse node -e
|
|
94
|
+
"const{Client}=require('pg');
|
|
95
|
+
const c=new Client({connectionString:process.env.ADMIN_URL,ssl:{rejectUnauthorized:false}});
|
|
96
|
+
(async()=>{
|
|
97
|
+
await c.connect();
|
|
98
|
+
await c.query('GRANT ALL ON SCHEMA public TO \"universe\"');
|
|
99
|
+
await c.query('GRANT ALL ON ALL TABLES IN SCHEMA public TO \"universe\"');
|
|
100
|
+
await c.query('GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO \"universe\"');
|
|
101
|
+
await c.end();
|
|
102
|
+
console.log('granted');
|
|
103
|
+
})().catch(e=>{console.error(e.message);process.exit(1)})"
|
|
104
|
+
args:
|
|
105
|
+
chdir: /opt/gravity
|
|
106
|
+
when: (pg_admin_url | default('')) | length > 0
|
|
107
|
+
no_log: true
|
|
108
|
+
register: grant_result
|
|
109
|
+
|
|
110
|
+
- name: "[3b/5] Schema rights"
|
|
111
|
+
debug:
|
|
112
|
+
msg: "{{ 'universe may create objects in its database' if (pg_admin_url | default('')) | length > 0 else 'skipped — bring-your-own database, permissions are yours' }}"
|
|
113
|
+
|
|
83
114
|
# Apply the .sql migrations with node-pg-migrate — same as `./unoverse db-setup`
|
|
84
115
|
# (scripts/lib/db-setup.sh). The old programmatic `require('./dist/db')` table
|
|
85
116
|
# setup is retired: the engine no longer ships a dist, and .sql migrations under
|
|
@@ -12,6 +12,19 @@ locals {
|
|
|
12
12
|
pg_pooled = local.pg_managed ? "postgresql://${digitalocean_database_user.universe[0].name}:${digitalocean_database_user.universe[0].password}@${digitalocean_database_connection_pool.universe[0].private_host}:${digitalocean_database_connection_pool.universe[0].port}/${digitalocean_database_connection_pool.universe[0].name}?sslmode=require&pgbouncer=true" : var.byo_postgres_url
|
|
13
13
|
pg_direct = local.pg_managed ? "postgresql://${digitalocean_database_user.universe[0].name}:${digitalocean_database_user.universe[0].password}@${local.pg_cluster_host}:${local.pg_cluster_port}/${digitalocean_database_db.universe[0].name}?sslmode=require" : var.byo_postgres_url
|
|
14
14
|
|
|
15
|
+
# THE ADMIN URL EXISTS FOR EXACTLY ONE STATEMENT. PostgreSQL 15+ no longer grants CREATE
|
|
16
|
+
# on schema public to PUBLIC, and DigitalOcean owns every database it creates as doadmin.
|
|
17
|
+
# So the universe user can connect and can create nothing: the first migration dies on
|
|
18
|
+
# "permission denied for schema public" after the extensions step reports OK, which reads
|
|
19
|
+
# like the database is fine. Only the cluster's admin can hand over those rights.
|
|
20
|
+
#
|
|
21
|
+
# It is a separate output and NOT part of env_production on purpose. The server holds the
|
|
22
|
+
# universe user's credentials and must never hold the cluster admin's — deploy reads this,
|
|
23
|
+
# spends it on one GRANT, and it goes no further.
|
|
24
|
+
pg_admin_user = local.pg_adopt ? data.digitalocean_database_cluster.existing_pg[0].user : (local.provision_pg ? digitalocean_database_cluster.pg[0].user : "")
|
|
25
|
+
pg_admin_pass = local.pg_adopt ? data.digitalocean_database_cluster.existing_pg[0].password : (local.provision_pg ? digitalocean_database_cluster.pg[0].password : "")
|
|
26
|
+
pg_admin_url = local.pg_managed ? "postgresql://${local.pg_admin_user}:${local.pg_admin_pass}@${local.pg_cluster_host}:${local.pg_cluster_port}/${digitalocean_database_db.universe[0].name}?sslmode=require" : ""
|
|
27
|
+
|
|
15
28
|
# Redis is always ours — provisioned above, no BYO branch.
|
|
16
29
|
redis_host = digitalocean_database_cluster.redis.private_host
|
|
17
30
|
redis_port = digitalocean_database_cluster.redis.port
|
|
@@ -39,6 +52,14 @@ output "api_url" {
|
|
|
39
52
|
value = local.has_domain ? "https://${local.api_host}" : "http://${digitalocean_loadbalancer.public.ip}"
|
|
40
53
|
}
|
|
41
54
|
|
|
55
|
+
# Read by deploy for the one-time schema grant, never written to the server. Empty when
|
|
56
|
+
# the database is BYO: somebody else's cluster, whose permissions are theirs to run.
|
|
57
|
+
output "pg_admin_url" {
|
|
58
|
+
value = local.pg_admin_url
|
|
59
|
+
description = "Cluster admin connection to this universe's database — used once, to grant the universe user rights on schema public"
|
|
60
|
+
sensitive = true
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
output "env_production" {
|
|
43
64
|
sensitive = true
|
|
44
65
|
description = "Rendered .env.production — complete, write to a file and deploy"
|
package/operator/lib/deploy.sh
CHANGED
|
@@ -363,6 +363,13 @@ cmd_deploy() {
|
|
|
363
363
|
|
|
364
364
|
_adopted_db_access "$cloud" grant
|
|
365
365
|
|
|
366
|
+
# The cluster admin connection, held in this shell and nowhere else. db-setup spends it
|
|
367
|
+
# on a single GRANT so the universe user can create objects in its own database; it is
|
|
368
|
+
# never written to the server, and an empty value (BYO database, or a ground without the
|
|
369
|
+
# output) simply skips that step.
|
|
370
|
+
local pg_admin_url
|
|
371
|
+
pg_admin_url=$(terraform -chdir="$ROOT/infra/$cloud" output -raw pg_admin_url 2>/dev/null)
|
|
372
|
+
|
|
366
373
|
|
|
367
374
|
# Read the deploy target from the rendered settings
|
|
368
375
|
local deploy_host deploy_user
|
|
@@ -429,11 +436,17 @@ EOF
|
|
|
429
436
|
# install.yml creates. The remedy was to know that `unoverse deploy init` exists, which
|
|
430
437
|
# is exactly the kind of knowledge this CLI is supposed to remove: deploy owns the whole
|
|
431
438
|
# journey, so it asks the server what it is and picks the right playbook itself.
|
|
439
|
+
# THE MARKER IS "SETUP FINISHED", NOT "A DIRECTORY EXISTS". Testing for /opt/gravity
|
|
440
|
+
# looked right and was not: install.yml creates that directory as its seventh task, so a
|
|
441
|
+
# first-time setup that then FAILED at the database step still left it behind. The next
|
|
442
|
+
# deploy saw it, decided the server was ready, shipped images onto a database that had
|
|
443
|
+
# never been migrated, and reported success. The stamp is written only after install,
|
|
444
|
+
# database and verify have all passed, so an interrupted setup resumes as a setup.
|
|
432
445
|
if [ -z "$subcommand" ]; then
|
|
433
446
|
if ! ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 \
|
|
434
|
-
"$deploy_user@$deploy_host" 'test -
|
|
447
|
+
"$deploy_user@$deploy_host" 'test -f /opt/gravity/.setup-complete' >/dev/null 2>&1; then
|
|
435
448
|
echo ""
|
|
436
|
-
info "
|
|
449
|
+
info "This server's setup has not finished. Running it ${DIM}(install, database, verify)${NC}"
|
|
437
450
|
subcommand="init"
|
|
438
451
|
fi
|
|
439
452
|
fi
|
|
@@ -470,6 +483,7 @@ EOF
|
|
|
470
483
|
-i "$tmp_inventory" \
|
|
471
484
|
"$ansible_dir/playbooks/db-setup.yml" \
|
|
472
485
|
-e "universe_root=$ROOT" \
|
|
486
|
+
-e "pg_admin_url=$pg_admin_url" \
|
|
473
487
|
-e "env_file=$env_prod" || { rm -f "$tmp_inventory"; fail "db setup failed — fix and re-run: unoverse deploy db"; exit 1; }
|
|
474
488
|
echo ""
|
|
475
489
|
info "[3/3] Verifying..."
|
|
@@ -478,6 +492,9 @@ EOF
|
|
|
478
492
|
"$ansible_dir/playbooks/test-connectivity.yml" \
|
|
479
493
|
-e "universe_root=$ROOT" \
|
|
480
494
|
-e "env_file=$env_prod" || { rm -f "$tmp_inventory"; fail "verification failed — inspect and re-run: unoverse deploy test"; exit 1; }
|
|
495
|
+
# Only now. Every step passed, so the next deploy can safely be an image push.
|
|
496
|
+
ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 \
|
|
497
|
+
"$deploy_user@$deploy_host" 'touch /opt/gravity/.setup-complete' >/dev/null 2>&1
|
|
481
498
|
echo ""
|
|
482
499
|
ok "Your universe is up. From now on, deploys are just: unoverse deploy"
|
|
483
500
|
info "Keeping it? Harden the VM when you're ready: unoverse deploy harden"
|
|
@@ -489,6 +506,7 @@ EOF
|
|
|
489
506
|
-i "$tmp_inventory" \
|
|
490
507
|
"$ansible_dir/playbooks/db-setup.yml" \
|
|
491
508
|
-e "universe_root=$ROOT" \
|
|
509
|
+
-e "pg_admin_url=$pg_admin_url" \
|
|
492
510
|
-e "env_file=$env_prod"
|
|
493
511
|
;;
|
|
494
512
|
test|check)
|
package/package.json
CHANGED