Skip to content

Initial migration of task backend to typed_sql #8784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions app/lib/database/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import 'dart:convert' show json;

import 'package:gcloud/service_scope.dart' as ss;
import 'package:json_annotation/json_annotation.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:postgres/postgres.dart';
import 'package:pub_dev/service/secret/backend.dart';
import 'package:pub_dev/shared/exceptions.dart';
import 'package:pub_dev/task/models.dart'
show AbortedTokenInfo, PackageVersionStateInfo;
Expand All @@ -17,6 +20,8 @@ export 'package:typed_sql/typed_sql.dart' hide AuthenticationException;
part 'model.g.dart';
part 'model.task.dart';

final _log = Logger('database');

/// Sets the active [database].
void registerDatabase(Database<PrimaryDatabase> value) =>
ss.register(#_database, value);
Expand All @@ -30,3 +35,55 @@ abstract final class PrimaryDatabase extends Schema {

Table<TaskDependency> get taskDependencies;
}

Future<DatabaseAdapter> setupDatabaseConnection() async {
final connectionString =
await secretBackend.lookup(SecretKey.databaseConnectionString);
if (connectionString == null) {
_log.shout('Missing database connection string from secrets');
throw AssertionError('Missing database connection string from secrets');
}
final u = Uri.parse(connectionString);
if (!u.isScheme('postgres')) {
_log.shout('Invalid database connection string scheme: ${u.scheme}');
throw AssertionError(
'Invalid database connection string scheme: ${u.scheme}');
}

if (u.host.isEmpty) {
throw AssertionError('Missing database host');
}

var database = u.path;
if (database.startsWith('/')) {
database = database.substring(1);
}

String? username, password;
final userInfo = u.userInfo;
if (userInfo.isNotEmpty) {
if (userInfo.split(':').length != 2) {
throw AssertionError(
'Invalid database connection string: unable to parse username/password',
);
}
username = userInfo.split(':').firstOrNull;
password = userInfo.split(':').lastOrNull;
}

return DatabaseAdapter.postgres(Pool.withEndpoints(
[
Endpoint(
host: u.host,
port: u.port == 0 ? 5432 : u.port,
database: database,
username: username,
password: password,
),
],
settings: PoolSettings(
applicationName: 'pub-dev',
maxConnectionCount: 10,
),
));
}
9 changes: 3 additions & 6 deletions app/lib/fake/backend/fake_database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:isolate' show Isolate;

import 'package:logging/logging.dart';
import 'package:pub_dev/database/model.dart';
import 'package:pub_dev/shared/env_config.dart';

DatabaseAdapter createFakeDatabaseAdaptor() {
return DatabaseAdapter.withLogging(
Expand All @@ -15,11 +16,8 @@ DatabaseAdapter createFakeDatabaseAdaptor() {
}

Future<DatabaseAdapter> _createFakeDatabaseAdaptor() async {
final isCI = !['false', '0', ''].contains(
Platform.environment['CI']?.toLowerCase() ?? '',
);
if (isCI) {
// ignore: invalid_use_of_visible_for_testing_member
if (envConfig.isContinuesIntegration) {
// Use default environment variables.
return DatabaseAdapter.postgresTestDatabase();
}

Expand Down Expand Up @@ -60,7 +58,6 @@ Future<DatabaseAdapter> _createFakeDatabaseAdaptor() async {
}
}

// ignore: invalid_use_of_visible_for_testing_member
return DatabaseAdapter.postgresTestDatabase(
host: socketPath,
database: 'postgres',
Expand Down
3 changes: 3 additions & 0 deletions app/lib/service/secret/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Secret extends db.Model {
abstract class SecretKey {
static const String redisConnectionString = 'redis-connection-string';

static const String databaseConnectionString = 'database-connection-string';

/// OAuth client secret.
static const String oauthClientSecret = 'oauth-client-secret';

Expand All @@ -38,6 +40,7 @@ abstract class SecretKey {
/// List of all keys.
static const values = [
redisConnectionString,
databaseConnectionString,
oauthClientSecret,
announcement,
uploadRestriction,
Expand Down
23 changes: 3 additions & 20 deletions app/lib/service/services.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async' show FutureOr, Zone;
import 'dart:io' show Platform;

import 'package:_pub_shared/utils/http.dart';
import 'package:appengine/appengine.dart';
Expand All @@ -15,7 +14,6 @@ import 'package:gcloud/service_scope.dart';
import 'package:gcloud/storage.dart';
import 'package:googleapis_auth/auth_io.dart' as auth;
import 'package:logging/logging.dart';
import 'package:postgres/postgres.dart' show Pool, Endpoint, PoolSettings;
import 'package:pub_dev/database/model.dart';
import 'package:pub_dev/fake/backend/fake_database.dart'
show createFakeDatabaseAdaptor;
Expand Down Expand Up @@ -92,23 +90,9 @@ Future<void> withServices(FutureOr<void> Function() fn) async {
final retryingAuthClient = httpRetryClient(innerClient: authClient);
registerScopeExitCallback(() async => retryingAuthClient.close());

final databaseAdapter = DatabaseAdapter.postgres(
Pool.withEndpoints(
[
Endpoint(
host: Platform.environment['PGHOST'] ?? '127.0.0.1',
port: int.tryParse(Platform.environment['PGPORT'] ?? '') ?? 5432,
database: Platform.environment['PGDATABASE'] ?? 'postgres',
username: Platform.environment['PGUSER'] ?? 'postgres',
password: Platform.environment['PGPASSWORD'] ?? 'postgres',
),
],
settings: PoolSettings(
applicationName: 'pub-dev',
maxConnectionCount: 10,
),
),
);
registerSecretBackend(GcpSecretBackend(authClient));

final databaseAdapter = await setupDatabaseConnection();
registerDatabase(Database<PrimaryDatabase>(
databaseAdapter,
SqlDialect.postgres(),
Expand All @@ -133,7 +117,6 @@ Future<void> withServices(FutureOr<void> Function() fn) async {
: loggingEmailSender,
);
registerUploadSigner(await createUploadSigner(retryingAuthClient));
registerSecretBackend(GcpSecretBackend(authClient));

// Configure a CloudCompute pool for later use in TaskBackend
//
Expand Down
5 changes: 5 additions & 0 deletions app/lib/shared/env_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ class _EnvConfig {
/// Youtube API key to use (skips Datastore secret).
late final youtubeApiKey = Platform.environment['YOUTUBE_API_KEY'];

/// True, if the process is running in a CI environment.
late final isContinuesIntegration = !['false', '0', ''].contains(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: rename to isContinuousIntegration or isCI?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though, I think we should rather have the explicit true (or maybe 1) check instead of negating the negative.

Platform.environment['CI']?.toLowerCase() ?? '',
);

/// Drives the logging environment in certain tests.
/// **Examples**:
/// * `DEBUG='*'`, will show output from all loggers.
Expand Down
Loading