| // Copyright 2019 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:async'; |
| |
| import 'package:meta/meta.dart'; |
| |
| import '../datastore/cocoon_config.dart'; |
| import '../foundation/providers.dart'; |
| import '../foundation/typedefs.dart'; |
| import '../foundation/utils.dart'; |
| import '../model/appengine/task.dart'; |
| import '../request_handling/api_request_handler.dart'; |
| import '../request_handling/authentication.dart'; |
| import '../request_handling/body.dart'; |
| import '../service/datastore.dart'; |
| import '../service/luci.dart'; |
| |
| @immutable |
| class RefreshChromebotStatus extends ApiRequestHandler<Body> { |
| const RefreshChromebotStatus( |
| Config config, |
| AuthenticationProvider authenticationProvider, { |
| @visibleForTesting LuciServiceProvider luciServiceProvider, |
| @visibleForTesting DatastoreServiceProvider datastoreProvider, |
| @visibleForTesting |
| this.branchHttpClientProvider = Providers.freshHttpClient, |
| @visibleForTesting this.gitHubBackoffCalculator = twoSecondLinearBackoff, |
| }) : luciServiceProvider = luciServiceProvider ?? _createLuciService, |
| datastoreProvider = |
| datastoreProvider ?? DatastoreService.defaultProvider, |
| assert(branchHttpClientProvider != null), |
| assert(gitHubBackoffCalculator != null), |
| super(config: config, authenticationProvider: authenticationProvider); |
| |
| final LuciServiceProvider luciServiceProvider; |
| final DatastoreServiceProvider datastoreProvider; |
| final HttpClientProvider branchHttpClientProvider; |
| final GitHubBackoffCalculator gitHubBackoffCalculator; |
| |
| static LuciService _createLuciService(ApiRequestHandler<dynamic> handler) { |
| return LuciService( |
| config: handler.config, |
| clientContext: handler.authContext.clientContext, |
| ); |
| } |
| |
| @override |
| Future<Body> get() async { |
| final LuciService luciService = luciServiceProvider(this); |
| final DatastoreService datastore = datastoreProvider(config.db); |
| final Map<BranchLuciBuilder, Map<String, List<LuciTask>>> luciTasks = |
| await luciService.getBranchRecentTasks( |
| repo: 'flutter', |
| requireTaskName: true, |
| ); |
| |
| for (BranchLuciBuilder branchLuciBuilder in luciTasks.keys) { |
| await runTransactionWithRetries(() async { |
| await _updateStatus( |
| branchLuciBuilder.luciBuilder, |
| branchLuciBuilder.branch, |
| datastore, |
| luciTasks[branchLuciBuilder], |
| ); |
| }); |
| } |
| return Body.empty; |
| } |
| |
| /// Update chromebot tasks statuses in datastore for [builder], |
| /// based on latest [luciTasks] statuses. |
| Future<void> _updateStatus( |
| LuciBuilder builder, |
| String branch, |
| DatastoreService datastore, |
| Map<String, List<LuciTask>> luciTasksMap) async { |
| final List<FullTask> datastoreTasks = await datastore |
| .queryRecentTasks(taskName: builder.taskName, branch: branch) |
| .toList(); |
| |
| /// Update [devicelabTask] when first [luciTask] run finishes. There may be |
| /// reruns for the same commit and same builder. Update [devicelabTask] |
| /// [builderNumberList] when luci rerun happens, and update [devicelabTask] |
| /// status when the status of latest luci run changes. |
| for (FullTask datastoreTask in datastoreTasks) { |
| if (luciTasksMap.containsKey(datastoreTask.commit.sha)) { |
| final List<LuciTask> luciTasks = luciTasksMap[datastoreTask.commit.sha]; |
| final String buildNumberList = luciTasks.reversed |
| .map((LuciTask luciTask) => luciTask.buildNumber.toString()) |
| .toList() |
| .join(','); |
| if (buildNumberList != datastoreTask.task.buildNumberList || |
| luciTasks.last.status != datastoreTask.task.status) { |
| final Task update = datastoreTask.task; |
| update.status = luciTasks.first.status; |
| update.buildNumberList = buildNumberList; |
| update.builderName = builder.name; |
| update.luciBucket = 'luci.flutter.prod'; |
| await datastore.insert(<Task>[update]); |
| } |
| } |
| } |
| } |
| } |