Cloud run dark launch (#605)
diff --git a/Dockerfile b/Dockerfile
index 4286ffa..2ad8f8d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -50,4 +50,7 @@
# the Dart app using custom script enabling debug modes.
CMD []
-ENTRYPOINT /bin/bash /dart_runtime/dart_run.sh
+ENTRYPOINT ["/dart_runtime/dart_run.sh", \
+ "--port", "8080", \
+ "--dark-launch", \
+ "--proxy-target", "https://dart-service-cloud-run-hdjctvyqtq-uc.a.run.app/"]
diff --git a/cloud_run.Dockerfile b/cloud_run.Dockerfile
index 26ebf5e..cfa6ee5 100644
--- a/cloud_run.Dockerfile
+++ b/cloud_run.Dockerfile
@@ -50,5 +50,4 @@
CMD []
ENTRYPOINT ["/dart_runtime/dart_cloud_run.sh", "--port", "${PORT}", \
- "--server-url", "http://0.0.0.0", \
"--redis-url", "redis://10.0.0.4:6379"]
diff --git a/lib/services_cloud_run.dart b/lib/services_cloud_run.dart
index 6e6b034..2eaf18b 100644
--- a/lib/services_cloud_run.dart
+++ b/lib/services_cloud_run.dart
@@ -23,15 +23,14 @@
final Logger _logger = Logger('services');
Future<void> main(List<String> args) async {
- final parser = ArgParser();
- parser.addOption('port', abbr: 'p');
- parser.addOption('server-url', defaultsTo: 'http://localhost');
- parser.addOption('redis-url');
- final result = parser.parse(args);
+ final parser = ArgParser()
+ ..addOption('port', abbr: 'p')
+ ..addOption('redis-url');
+ final results = parser.parse(args);
// Cloud Run supplies the port to bind to in the environment.
// Allow command line arg to override environment.
- final port = int.tryParse(result['port'] as String ?? '') ??
+ final port = int.tryParse(results['port'] as String ?? '') ??
int.tryParse(Platform.environment['PORT'] ?? '');
if (port == null) {
stdout.writeln('Could not parse port value from either environment '
@@ -39,7 +38,7 @@
exit(1);
}
- final redisServerUri = result['redis-url'] as String;
+ final redisServerUri = results['redis-url'] as String;
Logger.root.level = Level.FINER;
Logger.root.onRecord.listen((LogRecord record) {
diff --git a/lib/services_gae.dart b/lib/services_gae.dart
index 5dfd8b6..1cc7e1c 100644
--- a/lib/services_gae.dart
+++ b/lib/services_gae.dart
@@ -9,6 +9,7 @@
import 'dart:math';
import 'package:appengine/appengine.dart' as ae;
+import 'package:args/args.dart';
import 'package:logging/logging.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
@@ -28,8 +29,15 @@
final Logger _logger = Logger('gae_server');
void main(List<String> args) {
- var gaePort = 8080;
- if (args.isNotEmpty) gaePort = int.parse(args[0]);
+ final parser = ArgParser()
+ ..addFlag('dark-launch', help: 'Dark launch proxied compilation requests')
+ ..addOption('proxy-target',
+ help: 'URL base to proxy compilation requests to')
+ ..addOption('port',
+ abbr: 'p', defaultsTo: '8080', help: 'Port to attach to');
+ final results = parser.parse(args);
+
+ final gaePort = int.tryParse(results['port'] as String ?? '') ?? 8080;
if (SdkManager.sdk.sdkPath == null) {
throw 'No Dart SDK is available; set the DART_SDK env var.';
@@ -46,30 +54,33 @@
}
});
log.info('''Initializing dart-services:
- port: $gaePort
+ --port: $gaePort
+ --dark-launch: ${results['dark-launch']}
+ --proxy-target: ${results['proxy-target']}
sdkPath: ${SdkManager.sdk?.sdkPath}
- REDIS_SERVER_URI: ${io.Platform.environment['REDIS_SERVER_URI']}
- GAE_VERSION: ${io.Platform.environment['GAE_VERSION']}
+ \$REDIS_SERVER_URI: ${io.Platform.environment['REDIS_SERVER_URI']}
+ \$GAE_VERSION: ${io.Platform.environment['GAE_VERSION']}
''');
- final server = GaeServer(io.Platform.environment['REDIS_SERVER_URI']);
+ final server = GaeServer(
+ io.Platform.environment['REDIS_SERVER_URI'],
+ results['dark-launch'].toString().toLowerCase() == 'true',
+ results['proxy-target'].toString());
server.start(gaePort);
}
class GaeServer {
final String redisServerUri;
- bool discoveryEnabled;
CommonServerImpl commonServerImpl;
CommonServerApi commonServerApi;
- GaeServer(this.redisServerUri) {
+ GaeServer(this.redisServerUri, bool darkLaunch, String proxyTarget) {
hierarchicalLoggingEnabled = true;
recordStackTraceAtLevel = Level.SEVERE;
_logger.level = Level.ALL;
- discoveryEnabled = false;
commonServerImpl = CommonServerImpl(
GaeServerContainer(),
redisServerUri == null
@@ -79,6 +90,10 @@
io.Platform.environment['GAE_VERSION'],
),
);
+ if (proxyTarget != null && proxyTarget.isNotEmpty) {
+ commonServerImpl =
+ CommonServerImplProxy(commonServerImpl, darkLaunch, proxyTarget);
+ }
commonServerApi = CommonServerApi(commonServerImpl);
}
diff --git a/lib/src/common_server_impl.dart b/lib/src/common_server_impl.dart
index e523307..7b78e03 100644
--- a/lib/src/common_server_impl.dart
+++ b/lib/src/common_server_impl.dart
@@ -8,6 +8,7 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
+import 'package:http/http.dart' as http;
import 'package:logging/logging.dart';
import 'package:pedantic/pedantic.dart';
@@ -32,21 +33,161 @@
String get version;
}
-class CommonServerImpl {
- final ServerContainer container;
- final ServerCache cache;
+class CommonServerImplProxy implements CommonServerImpl {
+ const CommonServerImplProxy(
+ this._wrapped, this._darkLaunch, this._proxyTarget);
+ final CommonServerImpl _wrapped;
+ final String _proxyTarget;
+ final bool _darkLaunch;
- Compiler compiler;
- AnalysisServersWrapper analysisServers;
+ @override
+ Future<String> _checkCache(String query) => _wrapped._checkCache(query);
+
+ @override
+ Future<proto.CompileDDCResponse> _compileDDC(String source) =>
+ _wrapped._compileDDC(source);
+
+ @override
+ Future<proto.CompileResponse> _compileDart2js(String source,
+ {bool returnSourceMap = false}) =>
+ _wrapped._compileDart2js(source, returnSourceMap: returnSourceMap);
+
+ @override
+ Future<void> _setCache(String query, String result) =>
+ _wrapped._setCache(query, result);
+
+ @override
+ bool get analysisServersRunning => _wrapped.analysisServersRunning;
+
+ @override
+ Future<proto.AnalysisResults> analyze(proto.SourceRequest request) =>
+ _wrapped.analyze(request);
+
+ @override
+ Future<proto.AssistsResponse> assists(proto.SourceRequest request) =>
+ _wrapped.assists(request);
+
+ @override
+ Future<proto.CompileResponse> compile(proto.CompileRequest request) {
+ final url = '${_proxyTarget}api/dartservices/v2/compile';
+ final proxyResponse = http.post(url,
+ headers: {'Content-Type': 'application/json; charset=utf-8'},
+ body: _jsonEncoder.convert(request.toProto3Json()));
+ if (_darkLaunch) {
+ proxyResponse.then((response) {
+ log.info(
+ 'compile: Proxied request returned status code: ${response.statusCode}');
+ });
+ return _wrapped.compile(request);
+ } else {
+ return proxyResponse.then((response) async {
+ if (response.statusCode == 200) {
+ return proto.CompileResponse.create()
+ ..mergeFromProto3Json(JsonDecoder().convert(response.body));
+ } else {
+ final err = proto.BadRequest.create()
+ ..mergeFromProto3Json(JsonDecoder().convert(response.body));
+ throw BadRequest(err.error.message);
+ }
+ });
+ }
+ }
+
+ @override
+ Future<proto.CompileDDCResponse> compileDDC(proto.CompileDDCRequest request) {
+ final url = '${_proxyTarget}api/dartservices/v2/compileDDC';
+ final proxyResponse = http.post(url,
+ headers: {'Content-Type': 'application/json; charset=utf-8'},
+ body: _jsonEncoder.convert(request.toProto3Json()));
+ if (_darkLaunch) {
+ proxyResponse.then((response) {
+ log.info(
+ 'compileDDC: Proxied request returned status code: ${response.statusCode}');
+ });
+ return _wrapped.compileDDC(request);
+ } else {
+ return proxyResponse.then((response) async {
+ if (response.statusCode == 200) {
+ return proto.CompileDDCResponse.create()
+ ..mergeFromProto3Json(JsonDecoder().convert(response.body));
+ } else {
+ final err = proto.BadRequest.create()
+ ..mergeFromProto3Json(JsonDecoder().convert(response.body));
+ throw BadRequest(err.error.message);
+ }
+ });
+ }
+ }
+
+ @override
+ Future<proto.CompleteResponse> complete(proto.SourceRequest request) =>
+ _wrapped.complete(request);
+
+ @override
+ Future<proto.DocumentResponse> document(proto.SourceRequest request) =>
+ _wrapped.document(request);
+
+ @override
+ Future<proto.FixesResponse> fixes(proto.SourceRequest request) =>
+ _wrapped.fixes(request);
+
+ @override
+ Future<proto.FormatResponse> format(proto.SourceRequest request) =>
+ _wrapped.format(request);
+
+ @override
+ Future<void> init() => _wrapped.init();
+
+ @override
+ bool get isHealthy => _wrapped.isHealthy;
+
+ @override
+ bool get isRestarting => _wrapped.isRestarting;
+
+ @override
+ Future shutdown() => _wrapped.shutdown();
+
+ @override
+ Future<proto.VersionResponse> version(proto.VersionRequest request) =>
+ _wrapped.version(request);
+
+ @override
+ AnalysisServersWrapper get _analysisServers => _wrapped._analysisServers;
+
+ @override
+ Compiler get _compiler => _wrapped._compiler;
+
+ @override
+ ServerCache get _cache => _wrapped._cache;
+
+ @override
+ ServerContainer get _container => _wrapped._container;
+
+ @override
+ set _analysisServers(AnalysisServersWrapper __analysisServers) =>
+ _wrapped._analysisServers = __analysisServers;
+
+ @override
+ set _compiler(Compiler __compiler) => _wrapped._compiler = __compiler;
+}
+
+final JsonEncoder _jsonEncoder = const JsonEncoder.withIndent(' ');
+
+class CommonServerImpl {
+ final ServerContainer _container;
+ final ServerCache _cache;
+
+ Compiler _compiler;
+ AnalysisServersWrapper _analysisServers;
// Restarting and health status of the two Analysis Servers
- bool get analysisServersRunning => analysisServers.running;
- bool get isRestarting => analysisServers.isRestarting;
- bool get isHealthy => analysisServers.isHealthy;
+ bool get analysisServersRunning => _analysisServers.running;
+ bool get isRestarting => _analysisServers.isRestarting;
+ bool get isHealthy => _analysisServers.isHealthy;
CommonServerImpl(
- this.container,
- this.cache,
+ this._container,
+ this._cache,
) {
hierarchicalLoggingEnabled = true;
log.level = Level.ALL;
@@ -54,18 +195,18 @@
Future<void> init() async {
log.info('Beginning CommonServer init().');
- analysisServers = AnalysisServersWrapper();
- compiler = Compiler(SdkManager.sdk, SdkManager.flutterSdk);
+ _analysisServers = AnalysisServersWrapper();
+ _compiler = Compiler(SdkManager.sdk, SdkManager.flutterSdk);
- await compiler.warmup();
- await analysisServers.warmup();
+ await _compiler.warmup();
+ await _analysisServers.warmup();
}
Future<dynamic> shutdown() {
return Future.wait(<Future<dynamic>>[
- analysisServers.shutdown(),
- compiler.dispose(),
- Future<dynamic>.sync(cache.shutdown)
+ _analysisServers.shutdown(),
+ _compiler.dispose(),
+ Future<dynamic>.sync(_cache.shutdown)
]).timeout(const Duration(minutes: 1));
}
@@ -74,7 +215,7 @@
throw BadRequest('Missing parameter: \'source\'');
}
- return analysisServers.analyze(request.source);
+ return _analysisServers.analyze(request.source);
}
Future<proto.CompileResponse> compile(proto.CompileRequest request) {
@@ -102,7 +243,7 @@
throw BadRequest('Missing parameter: \'offset\'');
}
- return analysisServers.complete(request.source, request.offset);
+ return _analysisServers.complete(request.source, request.offset);
}
Future<proto.FixesResponse> fixes(proto.SourceRequest request) {
@@ -113,7 +254,7 @@
throw BadRequest('Missing parameter: \'offset\'');
}
- return analysisServers.getFixes(request.source, request.offset);
+ return _analysisServers.getFixes(request.source, request.offset);
}
Future<proto.AssistsResponse> assists(proto.SourceRequest request) {
@@ -124,7 +265,7 @@
throw BadRequest('Missing parameter: \'offset\'');
}
- return analysisServers.getAssists(request.source, request.offset);
+ return _analysisServers.getAssists(request.source, request.offset);
}
Future<proto.FormatResponse> format(proto.SourceRequest request) {
@@ -132,7 +273,7 @@
throw BadRequest('Missing parameter: \'source\'');
}
- return analysisServers.format(request.source, request.offset ?? 0);
+ return _analysisServers.format(request.source, request.offset ?? 0);
}
Future<proto.DocumentResponse> document(proto.SourceRequest request) async {
@@ -145,7 +286,7 @@
return proto.DocumentResponse()
..info.addAll(
- await analysisServers.dartdoc(request.source, request.offset) ??
+ await _analysisServers.dartdoc(request.source, request.offset) ??
<String, String>{});
}
@@ -156,7 +297,7 @@
..sdkVersionFull = SdkManager.sdk.versionFull
..runtimeVersion = vmVersion
..servicesVersion = servicesVersion
- ..appEngineVersion = container.version
+ ..appEngineVersion = _container.version
..flutterDartVersion = SdkManager.flutterSdk.version
..flutterDartVersionFull = SdkManager.flutterSdk.versionFull
..flutterVersion = SdkManager.flutterSdk.flutterVersion,
@@ -187,7 +328,7 @@
final watch = Stopwatch()..start();
final results =
- await compiler.compile(source, returnSourceMap: returnSourceMap);
+ await _compiler.compile(source, returnSourceMap: returnSourceMap);
if (results.hasOutput) {
final lineCount = source.split('\n').length;
@@ -239,7 +380,7 @@
log.info('CACHE: MISS for compileDDC');
final watch = Stopwatch()..start();
- final results = await compiler.compileDDC(source);
+ final results = await _compiler.compileDDC(source);
if (results.hasOutput) {
final lineCount = source.split('\n').length;
@@ -270,10 +411,10 @@
}
}
- Future<String> _checkCache(String query) => cache.get(query);
+ Future<String> _checkCache(String query) => _cache.get(query);
Future<void> _setCache(String query, String result) =>
- cache.set(query, result, expiration: _standardExpiration);
+ _cache.set(query, result, expiration: _standardExpiration);
}
String _printCompileProblem(CompilationProblem problem) => problem.message;
diff --git a/pubspec.lock b/pubspec.lock
index e675627..34cbbc2 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -154,7 +154,7 @@
name: codemirror
url: "https://pub.dartlang.org"
source: hosted
- version: "0.5.20+5.57.0"
+ version: "0.5.21+5.58.0"
collection:
dependency: transitive
description:
@@ -259,7 +259,7 @@
name: grpc
url: "https://pub.dartlang.org"
source: hosted
- version: "2.2.0"
+ version: "2.3.0"
html:
dependency: transitive
description:
@@ -280,7 +280,7 @@
name: http2
url: "https://pub.dartlang.org"
source: hosted
- version: "1.0.0"
+ version: "1.0.1"
http_methods:
dependency: transitive
description:
@@ -336,7 +336,7 @@
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
- version: "3.0.1"
+ version: "3.1.0"
logging:
dependency: "direct main"
description:
@@ -497,7 +497,7 @@
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
- version: "0.9.6"
+ version: "0.9.7+1"
source_map_stack_trace:
dependency: transitive
description:
@@ -567,7 +567,7 @@
name: test
url: "https://pub.dartlang.org"
source: hosted
- version: "1.15.3"
+ version: "1.15.4"
test_api:
dependency: transitive
description:
@@ -581,7 +581,7 @@
name: test_core
url: "https://pub.dartlang.org"
source: hosted
- version: "0.3.11"
+ version: "0.3.11+1"
timing:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index d93d726..0b250a7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -10,7 +10,7 @@
analyzer: 0.39.16
analysis_server_lib: ^0.1.4
appengine: ^0.12.0
- args: ^1.5.2
+ args: ^1.6.0
bazel_worker: ^0.1.23
crypto: ^2.0.0
dartis: ^0.5.0
diff --git a/tool/dart_run.sh b/tool/dart_run.sh
index 8453ba8..19bbd3f 100755
--- a/tool/dart_run.sh
+++ b/tool/dart_run.sh
@@ -17,4 +17,5 @@
${DBG_OPTION} \
--enable-vm-service:8181/0.0.0.0 \
${DART_VM_OPTIONS} \
- bin/server.dart
+ bin/server.dart \
+ $@