Skip to content

Commit

Permalink
feat: make system work in docker compose
Browse files Browse the repository at this point in the history
  • Loading branch information
eseidel committed Aug 11, 2024
1 parent b11c867 commit 41a4391
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 70 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ Cargo.lock

# MSVC Windows builds of rustc generate these, which store debugging information

Check warning on line 46 in .gitignore

View workflow job for this annotation

GitHub Actions / 🔤 Check Spelling / build

Unknown word (MSVC)

Check warning on line 46 in .gitignore

View workflow job for this annotation

GitHub Actions / 🔤 Check Spelling / build

Unknown word (rustc)
*.pdb

# Where postgres data is stored.
/db_data
47 changes: 47 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,58 @@
# cspell:words initdb healthcheck pg_isready
services:
web:
build: ./packages/ui
ports:
- "8080:80"
net:
build:
context: ./packages
dockerfile: ./cli/Dockerfile
args:
- BIN=network_execute
depends_on:
db:
condition: service_healthy
cli:
build:
context: ./packages
dockerfile: ./cli/Dockerfile
args:
- BIN=cli
environment:
- ST_AGENT=${ST_AGENT}
- ST_EMAIL=${ST_EMAIL}
- ST_FACTION=${ST_FACTION}
depends_on:
net:
condition: service_started
db:
condition: service_healthy
idle:
build:
context: ./packages
dockerfile: ./cli/Dockerfile
args:
- BIN=idle_queue
depends_on:
net:
condition: service_started
db:
condition: service_healthy
db:
image: postgres:15.3-alpine3.18
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=spacetraders
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
- ./db_data:/var/lib/postgresql/data
- ./packages/db/sql/tables:/docker-entrypoint-initdb.d/
1 change: 0 additions & 1 deletion cspell.config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ words:
- sublist
- codegen
- URANITE
- callsign
- writeln
- undocked
- ratelimit
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/bin/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ Future<void> cliMain(List<String> args) async {
// Use package:file to make things mockable.
const fs = LocalFileSystem();
final db = await defaultDatabase();
final token = await loadAuthTokenOrRegister(fs, db);
final agentName = Platform.environment['ST_AGENT'];
final email = Platform.environment['ST_EMAIL'];
final token =
await loadAuthTokenOrRegister(db, agentName: agentName, email: email);

// Api client should move to per-ship with a per-ship priority function.
final api = apiFromAuthToken(token, db);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/bin/idle_queue.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import 'package:cli/logic/idle_queue.dart';
import 'package:cli/net/auth.dart';

Future<void> command(FileSystem fs, Database db, ArgResults argResults) async {
final api = defaultApi(fs, db, getPriority: () => networkPriorityLow);
final api = await defaultApi(db, getPriority: () => networkPriorityLow);

final agent = await db.getAgent(symbol: config.agentSymbol);
if (agent == null) {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/bin/jumpgate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Future<void> command(FileSystem fs, Database db, ArgResults argResults) async {
final startSystemSymbol =
await startSystemFromArg(db, argResults.rest.firstOrNull);

final api = defaultApi(fs, db, getPriority: () => networkPriorityLow);
final api = await defaultApi(db, getPriority: () => networkPriorityLow);

final systemsCache = SystemsCache.load(fs);
final jumpGateSymbol = systemsCache
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/bin/scan_interesting_systems.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import 'package:cli/cli.dart';
import 'package:cli/net/auth.dart';

Future<void> command(FileSystem fs, Database db, ArgResults argResults) async {
final api = defaultApi(fs, db, getPriority: () => networkPriorityLow);
final api = await defaultApi(db, getPriority: () => networkPriorityLow);
final systemsCache = SystemsCache.load(fs);

final systems = await SystemsCache.loadOrFetch(fs);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/bin/systems_to_warp_to.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Future<void> command(FileSystem fs, Database db, ArgResults argResults) async {
const limit = 10;
const desiredMarketCount = 10;

final api = defaultApi(fs, db, getPriority: () => networkPriorityLow);
final api = await defaultApi(db, getPriority: () => networkPriorityLow);

final startSystemSymbol = await myHqSystemSymbol(db);

Expand Down
26 changes: 8 additions & 18 deletions packages/cli/lib/net/auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,11 @@ import 'package:cli/api.dart';
import 'package:cli/net/counts.dart';
import 'package:cli/net/queue.dart';
import 'package:db/db.dart';
import 'package:file/file.dart';
import 'package:types/types.dart';

export 'package:cli/net/queue.dart'
show networkPriorityDefault, networkPriorityLow;

/// The default path to the auth token.
const String defaultAuthTokenPath = 'data/auth_token.txt';

/// loadAuthToken loads the auth token from the given file system or
/// throws an error if it cannot be found.
String loadAuthToken(FileSystem fs, {String path = defaultAuthTokenPath}) {
final token = fs.file(path).readAsStringSync().trim();
if (token.isEmpty) {
throw Exception('No auth token found.');
}
return token;
}

/// Default priority function.
int defaultGetPriority() => networkPriorityDefault;

Expand Down Expand Up @@ -64,9 +50,13 @@ Api apiFromAuthToken(

/// defaultApi creates an Api with the default auth token read from the
/// given file system.
Api defaultApi(
FileSystem fs,
Future<Api> defaultApi(
Database db, {
int Function() getPriority = defaultGetPriority,
}) =>
apiFromAuthToken(loadAuthToken(fs), db, getPriority: getPriority);
}) async {
final token = await db.getAuthToken();
if (token == null) {
throw Exception('No auth token found.');
}
return apiFromAuthToken(token, db, getPriority: getPriority);
}
57 changes: 26 additions & 31 deletions packages/cli/lib/net/register.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:cli/caches.dart';
import 'package:cli/logger.dart';
import 'package:cli/net/auth.dart';
Expand All @@ -7,23 +9,17 @@ import 'package:types/types.dart';

/// loadAuthTokenOrRegister loads the auth token from the given file system
/// or registers a new user and returns the auth token.
Future<String> loadAuthTokenOrRegister(
FileSystem fs,
Database db, {
String? callsign,
String? email,
String path = defaultAuthTokenPath,
}) async {
try {
return loadAuthToken(fs);
} catch (e) {
Future<String> loadAuthTokenOrRegister(Database db,
{String? agentName, String? email}) async {
final token = await db.getAuthToken();
if (token != null) {
return token;
} else {
logger.info('No auth token found.');
// Otherwise, register a new user.
final handle = callsign ?? logger.prompt('What is your call sign?');
final token = await register(fs, db, callsign: handle, email: email);
final file = fs.file(path);
await file.create(recursive: true);
await file.writeAsString(token);
final name = agentName ?? logger.prompt('What is your agent name?');
final token = await register(db, agentName: name, email: email);
db.setAuthToken(token);
return token;
}
}
Expand All @@ -48,10 +44,10 @@ Future<String> _tryRegister(
/// If the call sign is already taken, it will prompt for the email address
/// associated with the call sign.
Future<String> register(
FileSystem fs,
Database db, {
required String callsign,
required String agentName,
String? email,
String? faction,
}) async {
final client = getApiClient(db);
final defaultApi = DefaultApi(client);
Expand All @@ -63,22 +59,21 @@ Future<String> register(

// There are more factions in the game than players are allowed to join
// at the start, so we use RegisterRequestFactionEnum.
final faction = logger.chooseOne(
'Choose a faction:',
choices: recruitingFactions,
display: (Faction faction) {
final f = factions.firstWhere((f) => f.symbol == faction.symbol);
// final reachable =
// clusterCache.connectedSystemCount(f.headquartersSymbol.systemSymbol);
return '${f.symbol}'; // - connected to $reachable systems';
},
);
final Faction chosenFaction;
if (faction != null) {
chosenFaction =
factions.firstWhere((f) => f.symbol == faction.toUpperCase());
} else {
logger.warn("Faction not specified. Choosing a random faction.");
chosenFaction =
recruitingFactions[Random().nextInt(recruitingFactions.length)];
}

try {
return await _tryRegister(
defaultApi,
symbol: callsign,
faction: faction.symbol,
symbol: agentName,
faction: chosenFaction.symbol,
email: email,
);
} on ApiException catch (e) {
Expand All @@ -96,8 +91,8 @@ Future<String> register(
);
return await _tryRegister(
defaultApi,
symbol: callsign,
faction: faction.symbol,
symbol: agentName,
faction: chosenFaction.symbol,
email: email,
);
} on ApiException catch (e) {
Expand Down
16 changes: 4 additions & 12 deletions packages/cli/test/net/auth_test.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import 'package:cli/net/auth.dart';
import 'package:cli/net/counts.dart';
import 'package:db/db.dart';
import 'package:file/memory.dart';
import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart';

class _MockDatabase extends Mock implements Database {}

void main() {
test('loadAuthToken', () {
final fs = MemoryFileSystem.test();
test('loadAuthToken', () async {
final db = _MockDatabase();
expect(() => loadAuthToken(fs), throwsException);
expect(() => defaultApi(fs, db), throwsException);

fs.file(defaultAuthTokenPath)
..createSync(recursive: true)
..writeAsStringSync('token\n\n');
expect(loadAuthToken(fs), 'token');

final api = defaultApi(fs, db);
expect(() => defaultApi(db), throwsException);
when(() => db.getAuthToken()).thenAnswer((_) async => 'token');
final api = await defaultApi(db);
expect(api.apiClient, isA<CountingApiClient>());
});
}
6 changes: 3 additions & 3 deletions packages/db/lib/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import 'package:postgres/postgres.dart' as pg;
// TODO(eseidel): Move this up to cli/config.dart.
/// Default database config for connecting to a local postgres database.
pg.Endpoint defaultDatabaseEndpoint = pg.Endpoint(
host: 'localhost',
database: 'spacetraders',
host: 'db',
username: 'postgres',
password: 'password',
password: 'postgres',
database: 'spacetraders',
);

/// Currently we use a docker container by default, which does not have its
Expand Down
14 changes: 14 additions & 0 deletions packages/db/lib/db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -651,4 +651,18 @@ class Database {
);
return result[0][0]! as int;
}

Future<String?> getAuthToken() async {
final result =
await executeSql('SELECT value FROM config WHERE key = \'auth_token\'');
if (result.isEmpty) {
return null;
}
return result[0][0] as String;
}

Future<void> setAuthToken(String token) async {
await executeSql('INSERT INTO config (key, value) VALUES (\'auth_token\', '
'\'$token\') ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value');
}
}
Empty file.
5 changes: 5 additions & 0 deletions packages/db/sql/tables/18_config.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- global configuration settings
CREATE TABLE config (
key TEXT NOT NULL PRIMARY KEY,
value TEXT NOT NULL
);

0 comments on commit 41a4391

Please sign in to comment.