Skip to content

Commit 0f759c6

Browse files
authored
feat: upsert the first storage node record with env var (#2087)
Also insert Sync 1.5 service with MySQL migration, and updated some docs. Closes STOR-487
1 parent dc07e83 commit 0f759c6

15 files changed

Lines changed: 219 additions & 88 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/src/config.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ The following configuration options are available.
9595

9696
| Env Var | Default Value | Description |
9797
| --- | --- | --- |
98+
| <span id="SYNC_TOKENSERVER__INIT_NODE_URL"></span>SYNC_TOKENSERVER__INIT_NODE_URL | None | The storage node URL, protocol + host, to insert into the `nodes` table on startup. This is the origin where the service is hosted, e.g. "http://localhost:8000". |
99+
| <span id="SYNC_TOKENSERVER__INIT_NODE_CAPACITY"></span>SYNC_TOKENSERVER__INIT_NODE_CAPACITY | 100000 | The storage node capacity of the server specified by `SYNC_TOKENSERVER__INIT_NODE_URL`. Only used if `SYNC_TOKENSERVER__INIT_NODE_URL` is set. |
98100
| <span id="SYNC_TOKENSERVER__ENABLED"></span>SYNC_TOKENSERVER__ENABLED | false | Enable tokenserver service |
99101
| <span id="SYNC_TOKENSERVER__RUN_MIGRATIONS"></span>SYNC_TOKENSERVER__RUN_MIGRATIONS | false | Run DB migrations on startup |
100102
| <span id="SYNC_TOKENSERVER__NODE_TYPE"></span>SYNC_TOKENSERVER__NODE_TYPE | spanner | Storage backend type reported in token response for telemetry. Valid values: "mysql", "postgres", "spanner" |

docs/src/how-to/how-to-run-with-docker.md

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ services:
4646
SYNC_TOKENSERVER__RUN_MIGRATIONS: "true"
4747
SYNC_TOKENSERVER__FXA_EMAIL_DOMAIN: "api.accounts.firefox.com"
4848
SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL: "https://oauth.accounts.firefox.com"
49+
SYNC_TOKENSERVER__INIT_NODE_URL: "${SYNC_TOKENSERVER__INIT_NODE_URL:-http://localhost:8000}"
4950
restart: unless-stopped
5051
healthcheck:
5152
test: ["CMD", "curl", "-f", "http://localhost:8000/__heartbeat__"]
@@ -56,9 +57,10 @@ services:
5657
```
5758
5859
Note that multiple values will be read from the environment:
59-
- `SYNC_MASTER_SECRET`: a secret used in cryptographic operationsk a passphrase or random character string, e.g. `use_your_own_secret_4d3d3d3d`
60-
- `SYNC_SYNCSTORAGE__DATABASE_URL`: database URL for syncstorage, e.g. `mysql://sync:test@example.io/syncstorage` or `postgres://testo:@localhost/syncdb`
61-
- `SYNC_TOKENSERVER__DATABASE_URL`: database URL for tokenserver, e.g. `mysql://sync:test@example.io/tokenserver` or `postgres://testo:@localhost/syncdb`
60+
- [`SYNC_MASTER_SECRET`](../config.md#SYNC_MASTER_SECRET): a secret used in cryptographic operations, a passphrase or random character string, e.g. `use_your_own_secret_4d3d3d3d`
61+
- [`SYNC_SYNCSTORAGE__DATABASE_URL`](../config.md#SYNC_SYNCSTORAGE__DATABASE_URL): database URL for syncstorage, e.g. `mysql://sync:test@example.io/syncstorage` or `postgres://testo:@localhost/syncdb`
62+
- [`SYNC_TOKENSERVER__DATABASE_URL`](../config.md#SYNC_TOKENSERVER__DATABASE_URL): database URL for tokenserver, e.g. `mysql://sync:test@example.io/tokenserver` or `postgres://testo:@localhost/syncdb`
63+
- [`SYNC_TOKENSERVER__INIT_NODE_URL`](../config.md#SYNC_TOKENSERVER__INIT_NODE_URL): the storage node URL (defaults to `http://localhost:8000`). Replace with the actual URL where clients will access the sync server.
6264

6365
The values can be directly written into the yaml as well.
6466

@@ -68,46 +70,13 @@ Next, start the service with `docker compose`:
6870
SYNC_MASTER_SECRET=use_your_own_secret_4d3d3d3d \
6971
SYNC_SYNCSTORAGE__DATABASE_URL="mysql://sync:test@example.io/syncstorage" \
7072
SYNC_TOKENSERVER__DATABASE_URL="mysql://sync:test@example.io/tokenserver" \
73+
SYNC_TOKENSERVER__INIT_NODE_URL="http://localhost:8000" \
7174
docker compose -f docker-compose.yaml up -d
7275
```
7376

74-
### Database Bootstrapping
75-
76-
After starting the service on a clean, uninitialized database, some bootstrapping records need to be inserted.
77-
78-
For MySQL, run
79-
```sql
80-
INSERT INTO tokenserver.services (service, pattern) VALUES ('sync-1.5', '{node}/1.5/{uid}');
81-
82-
INSERT INTO tokenserver.nodes (service, node, available, current_load, capacity, downed, backoff)
83-
VALUES (
84-
(SELECT id FROM services WHERE service = 'sync-1.5'),
85-
'http://localhost:8000',
86-
1, 0, 1000, 0, 0
87-
);
88-
```
89-
90-
For PostgreSQL, run
91-
```sql
92-
INSERT INTO nodes (service, node, available, current_load, capacity, downed, backoff)
93-
VALUES (
94-
(SELECT id FROM services WHERE service = 'sync-1.5'),
95-
'http://localhost:8000',
96-
1, 0, 1000, 0, 0
97-
);
98-
```
99-
100-
Note that `http://localhost:8000` above needs to be replaced with the actual
101-
service URL.
102-
103-
Restart the service with
104-
```sh
105-
docker compose -f docker-compose.yaml restart
106-
```
107-
10877
## Docker Compose, One-Shot with PostgreSQL
10978

110-
Alternatively, the database can be started through `docker compose` as well. The real service URL can be set with the `NODE_URL` environment variable.
79+
Alternatively, the database can be started through `docker compose` as well. The real service URL can be set with the `INIT_NODE_URL` environment variable.
11180

11281
Save the yaml below into a file, e.g. `docker-compose.one-shot.yaml`.
11382

@@ -131,6 +100,7 @@ services:
131100
SYNC_TOKENSERVER__FXA_OAUTH_SERVER_URL: "https://oauth.accounts.firefox.com"
132101
SYNC_HUMAN_LOGS: "${SYNC_HUMAN_LOGS:-false}"
133102
RUST_LOG: "${RUST_LOG:-info}"
103+
SYNC_TOKENSERVER__INIT_NODE_URL: "${SYNC_TOKENSERVER__INIT_NODE_URL:-http://localhost:8000}"
134104
depends_on:
135105
postgres:
136106
condition: service_healthy
@@ -159,32 +129,6 @@ services:
159129
start_period: 30s
160130
restart: unless-stopped
161131
162-
# insert record for the storage node
163-
bootstrap:
164-
image: postgres:18
165-
container_name: syncserver-bootstrap
166-
environment:
167-
PGHOST: postgres
168-
PGPORT: 5432
169-
PGUSER: sync
170-
PGPASSWORD: sync
171-
PGDATABASE: syncserver
172-
NODE_URL: "${NODE_URL:-http://localhost:8000}"
173-
depends_on:
174-
syncserver:
175-
condition: service_healthy
176-
command: >
177-
sh -c "
178-
echo 'Waiting for migrations to complete...';
179-
sleep 5;
180-
echo 'Inserting storage node record...';
181-
psql -c \"INSERT INTO nodes (service, node, available, current_load, capacity, downed, backoff)
182-
VALUES ((SELECT id FROM services WHERE service = 'sync-1.5'), '\$\${NODE_URL}', 1, 0, 1000, 0, 0)
183-
ON CONFLICT (service, node) DO NOTHING;\";
184-
echo 'DB bootstrap complete';
185-
"
186-
restart: "no"
187-
188132
volumes:
189133
postgres_data:
190134
driver: local
@@ -194,7 +138,7 @@ Next, start the service with `docker compose`:
194138

195139
```sh
196140
SYNC_MASTER_SECRET=use_your_own_secret_4d3d3d3d \
197-
NODE_URL=http://localhost:8000 \
141+
SYNC_TOKENSERVER__INIT_NODE_URL=http://localhost:8000 \
198142
docker compose -f docker-compose.one-shot.yaml up -d
199143
```
200144

tokenserver-db-common/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ pub trait Db {
6969
/// Show database uptime status and health as boolean.
7070
async fn check(&mut self) -> DbResult<results::Check>;
7171

72+
/// Insert an initial Sync 1.5 node record. Does nothing when there is a conflict.
73+
async fn insert_sync15_node(&mut self, params: params::Sync15Node) -> DbResult<()>;
74+
7275
/// Get Node ID based on service_id and node string.
7376
async fn get_node_id(&mut self, params: params::GetNodeId) -> DbResult<results::GetNodeId>;
7477

tokenserver-db-common/src/params.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,12 @@ pub struct RemoveNode {
126126

127127
#[cfg(debug_assertions)]
128128
pub type SpannerNodeId = Option<i32>;
129+
130+
pub struct Sync15Node {
131+
pub node: String,
132+
pub capacity: i32,
133+
}
134+
135+
impl Sync15Node {
136+
pub const SERVICE_NAME: &str = "sync-1.5";
137+
}

tokenserver-db/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ url = "2.5"
1818

1919
[dev-dependencies]
2020
env_logger.workspace = true
21+
temp-env.workspace = true
2122

2223
syncserver-settings = { path = "../syncserver-settings" }
2324

tokenserver-db/src/mock.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ impl Db for MockDb {
113113
Ok(results::GetServiceId::default())
114114
}
115115

116+
async fn insert_sync15_node(&mut self, _params: params::Sync15Node) -> Result<(), DbError> {
117+
Ok(())
118+
}
119+
116120
fn metrics(&self) -> &Metrics {
117121
static METRICS: LazyLock<Metrics> = LazyLock::new(Metrics::noop);
118122
&METRICS

tokenserver-db/src/tests.rs

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,88 @@ async fn post_user() -> DbResult<()> {
399399
Ok(())
400400
}
401401

402+
#[tokio::test]
403+
async fn test_init_sync15_node() -> DbResult<()> {
404+
temp_env::async_with_vars(
405+
[
406+
(
407+
"SYNC_TOKENSERVER__INIT_NODE_URL",
408+
Some("https://testo.example.gg"),
409+
),
410+
("SYNC_TOKENSERVER__INIT_NODE_CAPACITY", Some("38383")),
411+
],
412+
async {
413+
let pool = db_pool().await?;
414+
let mut db = pool.get().await?;
415+
416+
let service_id = db
417+
.get_service_id(params::GetServiceId {
418+
service: params::Sync15Node::SERVICE_NAME.to_owned(),
419+
})
420+
.await?
421+
.id;
422+
423+
let node_id = db
424+
.get_node_id(params::GetNodeId {
425+
service_id,
426+
node: "https://testo.example.gg".to_owned(),
427+
})
428+
.await?
429+
.id;
430+
431+
let node = db.get_node(params::GetNode { id: node_id }).await?;
432+
433+
assert_eq!(node.node, "https://testo.example.gg");
434+
assert_eq!(node.capacity, 38383);
435+
assert_eq!(node.available, 1);
436+
assert_eq!(node.current_load, 0);
437+
438+
Ok(())
439+
},
440+
)
441+
.await
442+
}
443+
444+
#[tokio::test]
445+
async fn test_init_sync15_node_with_default_capacity() -> DbResult<()> {
446+
temp_env::async_with_vars(
447+
[
448+
(
449+
"SYNC_TOKENSERVER__INIT_NODE_URL",
450+
Some("https://testo.example.gg"),
451+
),
452+
("SYNC_TOKENSERVER__INIT_NODE_CAPACITY", None::<&str>),
453+
],
454+
async {
455+
let pool = db_pool().await?;
456+
let mut db = pool.get().await?;
457+
458+
let service_id = db
459+
.get_service_id(params::GetServiceId {
460+
service: params::Sync15Node::SERVICE_NAME.to_owned(),
461+
})
462+
.await?
463+
.id;
464+
465+
let node_id = db
466+
.get_node_id(params::GetNodeId {
467+
service_id,
468+
node: "https://testo.example.gg".to_owned(),
469+
})
470+
.await?
471+
.id;
472+
473+
let node = db.get_node(params::GetNode { id: node_id }).await?;
474+
475+
assert_eq!(node.node, "https://testo.example.gg");
476+
assert_eq!(node.capacity, 100000);
477+
478+
Ok(())
479+
},
480+
)
481+
.await
482+
}
483+
402484
#[tokio::test]
403485
async fn get_node_id() -> DbResult<()> {
404486
let pool = db_pool().await?;
@@ -1352,28 +1434,5 @@ async fn db_pool() -> DbResult<Box<dyn DbPool>> {
13521434
)?;
13531435
pool.init().await?;
13541436

1355-
if settings.tokenserver.database_url.starts_with("mysql://") {
1356-
// Ensure the "sync-1.5" service
1357-
// TODO: tokenserver-mysql's migration should add this service
1358-
// entry for us (if possible)
1359-
let mut db = pool.get().await?;
1360-
let service = "sync-1.5".to_owned();
1361-
let result = db
1362-
.get_service_id(params::GetServiceId {
1363-
service: service.clone(),
1364-
})
1365-
.await;
1366-
if let Err(e) = result {
1367-
if !e.is_diesel_not_found() {
1368-
return Err(e);
1369-
}
1370-
db.post_service(params::PostService {
1371-
service,
1372-
pattern: "{node}/1.5/{uid}".to_owned(),
1373-
})
1374-
.await?;
1375-
}
1376-
}
1377-
13781437
Ok(pool)
13791438
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
DELETE FROM services WHERE service = 'sync-1.5';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- The standard Sync service entry
2+
INSERT IGNORE INTO services (service, pattern) VALUES
3+
('sync-1.5', '{node}/1.5/{uid}');

0 commit comments

Comments
 (0)