blob: fecf59f203c9d81be38a2ce08ff65258634042b6 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
library symbols_repo;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:googleapis_auth/auth_io.dart' as auth;
import 'package:http/http.dart' as http;
import 'package:gcloud/storage.dart';
import '../build.dart';
import '../common_util.dart';
import '../crash_handling.dart' show KnownFailure;
import 'cache.dart';
// ignore: avoid_classes_with_only_static_members
/// Provides a singleton `http.Client` instance that is authorized with
/// Google Cloud.
class GoogleApiClient {
/// Obtains a cached client. The client should be closed by
/// calling `GoogleApiClient.close` on exit.
static Future<http.Client> getDefault() {
if (_client != null) {
return _client;
}
return _client = _getDefault();
}
static Future<void> close() async {
if (_client != null) {
final client = await _client;
client.close();
_client = null;
}
if (_baseClient != null) {
_baseClient.close();
_baseClient = null;
}
}
static Future<http.Client> _getDefault() async {
final homeDir = Directory(Platform.environment['HOME']);
final File credentialFile =
homeDir / File('.config/gcloud/application_default_credentials.json');
if (!credentialFile.existsSync()) {
throw KnownFailure('''
Cannot find Google Cloud Application Default Credentials.
Please install the Google Cloud SDK via:
sudo apt install -y google-cloud-sdk
and run the following command to obtain the credentials:
gcloud auth application-default login
See https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login
''');
}
final jsonCredentials = jsonDecode(await credentialFile.readAsString());
final scopes = <String>[]..addAll(Storage.SCOPES);
final baseClient = http.Client();
// By default, running `gcloud auth application-default login` yields
// a credential of the "authorized_user" type. We need to exchange that
// for a crediential that is supported by the dart gcloud library.
final userCred = auth.AccessCredentials(
auth.AccessToken(jsonCredentials['type'], '', DateTime.now().toUtc()),
jsonCredentials['refresh_token'],
scopes);
final appCred = await auth.refreshCredentials(
auth.ClientId(
jsonCredentials['client_id'],
jsonCredentials['client_secret'],
),
userCred,
baseClient);
// Stow away baseClient to close later. Closing the authenticated
// client would not close the base client.
_baseClient = baseClient;
return auth.authenticatedClient(baseClient, appCred);
}
static Future<http.Client> _client;
static http.Client _baseClient;
}
class CloudRepo {
CloudRepo(this.client, this.bucket, this.namespace, this.cache);
static Future<CloudRepo> create(String gcsUrl, Cache cache) async {
final storage = Storage(await GoogleApiClient.getDefault(), null);
final uri = Uri.parse(gcsUrl);
final host = uri.host;
final namespace = removePrefix(uri.path, '/');
final bucket = storage.bucket(host);
return CloudRepo(storage, bucket, namespace, cache);
}
Future<String> getBuildObject(String buildId) async {
final maybeEntry = await cache.getEntry(buildId);
if (maybeEntry != null) {
return maybeEntry;
}
final stream = bucket.read('$namespace/$buildId.debug');
return cache.addEntry(buildId, stream);
}
final Storage client;
final Bucket bucket;
final String namespace;
final Cache cache;
}