blob: 1e7bdc262a2a10733563dbcf71a63c1582f22eab [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Heads up! Everything in this file is still in flux. The new reboot checks API
* is still in development. So everything in this file can change. And it is
* expected that the amount of comments and tests is limited for the time being.
*/
function pluralize(count, noun) {
if (count === 0) return '';
return `${count} ${noun}` + (count > 1 ? 's' : '');
}
function generateDurationString(startTime, endTime) {
const secondsAgo = Math.round((endTime - startTime) / 1000);
if (secondsAgo === 0) {
return ZERO_SECONDS;
}
const durationSegments = [];
if (secondsAgo % 60 !== 0) {
durationSegments.push(`${secondsAgo % 60} sec`);
}
const minutesAgo = Math.floor(secondsAgo / 60);
if (minutesAgo % 60 !== 0) {
durationSegments.push(`${minutesAgo % 60} min`);
}
const hoursAgo = Math.floor(minutesAgo / 60);
if (hoursAgo % 24 !== 0) {
const hours = pluralize(hoursAgo % 24, 'hour', 'hours');
durationSegments.push(`${hours}`);
}
const daysAgo = Math.floor(hoursAgo / 24);
if (daysAgo % 30 !== 0) {
const days = pluralize(daysAgo % 30, 'day', 'days');
durationSegments.push(`${days}`);
}
const monthsAgo = Math.floor(daysAgo / 30);
if (monthsAgo > 0) {
const months = pluralize(monthsAgo, 'month', 'months');
durationSegments.push(`${months}`);
}
return durationSegments.reverse().slice(0, 2).join(' ');
}
function computeDuration(check) {
if (!check.started || !check.finished) {
return '-';
}
const startTime = new Date(check.started);
const finishTime = check.finished ? new Date(check.finished) : new Date();
return generateDurationString(startTime, finishTime);
}
export class RebootFetcher {
constructor(restApi) {
this.restApi = restApi;
}
async fetchCurrent() {
return this.fetch(this.changeNumber, this.patchsetNumber);
}
async fetch(changeData) {
const {changeNumber, patchsetNumber} = changeData;
this.changeNumber = changeNumber;
this.patchsetNumber = patchsetNumber;
const checks = await this.apiGet('?o=CHECKER');
return {
responseCode: 'OK',
runs: checks.map(check => this.convert(check)),
};
}
async apiGet(suffix) {
return this.restApi.get(
'/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber
+ '/checks' + suffix);
}
async apiPost(suffix) {
return this.restApi.post(
'/changes/' + this.changeNumber + '/revisions/' + this.patchsetNumber
+ '/checks' + suffix);
}
/**
* Converts a Checks Plugin CheckInfo object into a Reboot Checks API Run
* object.
*
* TODO(brohlfs): Refine this conversion and add tests.
*/
convert(check) {
let status = 'RUNNABLE';
if (check.state === 'RUNNING' || check.state === 'SCHEDULED') {
status = 'RUNNING';
} else if (check.state === 'FAILED' || check.state === 'SUCCESSFUL') {
status = 'COMPLETED';
}
const run = {
checkName: check.checker_name,
checkDescription: check.checker_description,
externalId: check.checker_uuid,
status,
};
if (check.started) run.startedTimestamp = new Date(check.started);
if (check.finished) run.finishedTimestamp = new Date(check.finished);
if (status === 'RUNNING') {
run.statusDescription = check.message;
if (check.url) {
run.statusLink = check.url;
}
} else if (check.state === 'SUCCESSFUL') {
run.statusDescription =
check.message || `Passed (${computeDuration(check)})`;
if (check.url) {
run.statusLink = check.url;
}
} else if (check.state === 'FAILED') {
run.results = [{
category: 'ERROR',
summary: check.message || `Failed (${computeDuration(check)})`,
}];
if (check.url) {
run.results[0].links = [{
url: check.url,
primary: true,
icon: 'EXTERNAL',
}];
}
}
if (status !== 'RUNNING') {
run.actions = [{
name: 'Run',
primary: true,
callback: () => this.run(check.checker_uuid),
}];
}
return run;
}
run(uuid) {
return this.apiPost('/' + uuid + '/rerun')
.catch(e => {
return {errorMessage: `Triggering the run failed: ${e.message}`};
});
}
}