harbor/src/portal/lib/src/vulnerability-scanning/result-bar-chart.component.ts

199 lines
6.3 KiB
TypeScript

import {
Component,
Input,
OnInit,
OnDestroy,
ChangeDetectorRef,
} from '@angular/core';
import { Subscription , timer} from "rxjs";
import { clone, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from '../utils';
import {
VulnerabilitySummary,
TagService,
ScanningResultService,
Tag
} from '../service/index';
import { ErrorHandler } from '../error-handler/index';
import { ChannelService } from '../channel/index';
import { JobLogService } from "../service/index";
const STATE_CHECK_INTERVAL: number = 3000; // 3s
const RETRY_TIMES: number = 3;
@Component({
selector: 'hbr-vulnerability-bar',
templateUrl: './result-bar-chart-component.html',
styleUrls: ['./scanning.scss']
})
export class ResultBarChartComponent implements OnInit, OnDestroy {
@Input() repoName: string = "";
@Input() tagId: string = "";
@Input() summary: VulnerabilitySummary;
onSubmitting: boolean = false;
retryCounter: number = 0;
stateCheckTimer: Subscription;
scanSubscription: Subscription;
timerHandler: any;
constructor(
private tagService: TagService,
private scanningService: ScanningResultService,
private errorHandler: ErrorHandler,
private channel: ChannelService,
private ref: ChangeDetectorRef,
private jobLogService: JobLogService,
) { }
ngOnInit(): void {
if ((this.status === VULNERABILITY_SCAN_STATUS.RUNNING ||
this.status === VULNERABILITY_SCAN_STATUS.PENDING) &&
!this.stateCheckTimer) {
// Avoid duplicated subscribing
this.stateCheckTimer = timer(0, STATE_CHECK_INTERVAL).subscribe(() => {
this.getSummary();
});
}
this.scanSubscription = this.channel.scanCommand$.subscribe((tagId: string) => {
let myFullTag: string = this.repoName + "/" + this.tagId;
if (myFullTag === tagId) {
this.scanNow();
}
});
}
ngOnDestroy(): void {
if (this.stateCheckTimer) {
this.stateCheckTimer.unsubscribe();
this.stateCheckTimer = null;
}
if (this.scanSubscription) {
this.scanSubscription.unsubscribe();
}
}
// Get vulnerability scanning status
public get status(): string {
if (this.summary && this.summary.scan_status) {
return this.summary.scan_status;
}
return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
}
public get completed(): boolean {
return this.status === VULNERABILITY_SCAN_STATUS.SUCCESS;
}
public get error(): boolean {
return this.status === VULNERABILITY_SCAN_STATUS.ERROR;
}
public get queued(): boolean {
return this.status === VULNERABILITY_SCAN_STATUS.PENDING;
}
public get scanning(): boolean {
return this.status === VULNERABILITY_SCAN_STATUS.RUNNING;
}
public get otherStatus(): boolean {
return !(this.completed || this.error || this.queued || this.scanning);
}
scanNow(): void {
if (this.onSubmitting) {
// Avoid duplicated submitting
console.log("duplicated submit");
return;
}
if (!this.repoName || !this.tagId) {
console.log("bad repository or tag");
return;
}
this.onSubmitting = true;
this.scanningService.startVulnerabilityScanning(this.repoName, this.tagId)
.subscribe(() => {
this.onSubmitting = false;
// Forcely change status to queued after successful submitting
this.summary = {
scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
};
// Forcely refresh view
this.forceRefreshView(1000);
// Start check status util the job is done
if (!this.stateCheckTimer) {
// Avoid duplicated subscribing
this.stateCheckTimer = timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => {
this.getSummary();
});
}
}, error => {
this.onSubmitting = false;
this.errorHandler.error(error);
});
}
getSummary(): void {
if (!this.repoName || !this.tagId) {
return;
}
this.tagService.getTag(this.repoName, this.tagId)
.subscribe((t: Tag) => {
// To keep the same summary reference, use value copy.
if (t.scan_overview) {
this.copyValue(t.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
}
// Forcely refresh view
this.forceRefreshView(1000);
if (!this.queued && !this.scanning) {
// Scanning should be done
if (this.stateCheckTimer) {
this.stateCheckTimer.unsubscribe();
this.stateCheckTimer = null;
}
}
this.channel.tagDetail$.next(t);
}, error => {
this.errorHandler.error(error);
this.retryCounter++;
if (this.retryCounter >= RETRY_TIMES) {
// Stop timer
if (this.stateCheckTimer) {
this.stateCheckTimer.unsubscribe();
this.stateCheckTimer = null;
}
this.retryCounter = 0;
}
});
}
copyValue(newVal: VulnerabilitySummary): void {
if (!this.summary || !newVal || !newVal.scan_status) { return; }
this.summary = clone(newVal);
}
forceRefreshView(duration: number): void {
// Reset timer
if (this.timerHandler) {
clearInterval(this.timerHandler);
}
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
setTimeout(() => {
if (this.timerHandler) {
clearInterval(this.timerHandler);
this.timerHandler = null;
}
}, duration);
}
viewLog(): string {
return `/api/repositories/${this.repoName}/tags/${this.tagId}/scan/${this.summary.report_id}/log`;
}
}