mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-29 12:07:56 +01:00
Improve scan all page
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
923a538570
commit
75da08303a
@ -179,6 +179,11 @@ export class ScanningMetrics {
|
|||||||
[key: string]: number;
|
[key: string]: number;
|
||||||
};
|
};
|
||||||
requester?: string;
|
requester?: string;
|
||||||
isScheduled?: boolean;
|
trigger?: string;
|
||||||
ongoing: boolean;
|
ongoing: boolean;
|
||||||
}
|
}
|
||||||
|
export enum Triggers {
|
||||||
|
MANUAL= 'Manual',
|
||||||
|
SCHEDULE = 'Schedule',
|
||||||
|
EVENT = 'Event'
|
||||||
|
}
|
||||||
|
@ -51,12 +51,7 @@ export class ScanAllRepoService {
|
|||||||
|
|
||||||
return this.scanApiRepository.putSchedule(param);
|
return this.scanApiRepository.putSchedule(param);
|
||||||
}
|
}
|
||||||
getScheduleMetrics(): Observable<ScanningMetrics> {
|
getMetrics(): Observable<ScanningMetrics> {
|
||||||
return this.http.get(CURRENT_BASE_HREF + '/scans/schedule/metrics')
|
|
||||||
.pipe(catchError(error => observableThrowError(error)))
|
|
||||||
.pipe(map(response => response as ScanningMetrics));
|
|
||||||
}
|
|
||||||
getManualMetrics(): Observable<ScanningMetrics> {
|
|
||||||
return this.http.get(CURRENT_BASE_HREF + '/scans/all/metrics')
|
return this.http.get(CURRENT_BASE_HREF + '/scans/all/metrics')
|
||||||
.pipe(catchError(error => observableThrowError(error)))
|
.pipe(catchError(error => observableThrowError(error)))
|
||||||
.pipe(map(response => response as ScanningMetrics));
|
.pipe(map(response => response as ScanningMetrics));
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clr-col" *ngIf="scanningMetrics && scanningMetrics.total">
|
<div class="clr-col" *ngIf="scanningMetrics && scanningMetrics.total">
|
||||||
<div class="total" [style.width]="totalWidth+'px'">
|
<div class="total" [style.width]="totalWidth+'px'">
|
||||||
<span class="float-left" *ngIf="!scanningMetrics?.isScheduled">{{ 'CONFIG.SCANNING.MANUAL' | translate }}</span>
|
<span class="float-left" *ngIf="isManual()">{{ 'CONFIG.SCANNING.MANUAL' | translate }}</span>
|
||||||
<span class="float-left" *ngIf="scanningMetrics?.isScheduled">{{ 'CONFIG.SCANNING.SCHEDULED' | translate }}</span>
|
<span class="float-left" *ngIf="isSchedule()">{{ 'CONFIG.SCANNING.SCHEDULED' | translate }}</span>
|
||||||
<span>{{ 'SCANNER.TOTAL' | translate }}</span>
|
<span>{{ 'SCANNER.TOTAL' | translate }}</span>
|
||||||
<span class="margin-left-5">{{scanningMetrics?.total}}</span>
|
<span class="margin-left-5">{{scanningMetrics?.total}}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -32,13 +32,15 @@
|
|||||||
<div class="error h-100" [style.width]="errorWidth()"></div>
|
<div class="error h-100" [style.width]="errorWidth()"></div>
|
||||||
<div class="finished h-100" [style.width]="finishedWidth()"></div>
|
<div class="finished h-100" [style.width]="finishedWidth()"></div>
|
||||||
<div class="in-progress h-100" [style.width]="runningWidth()"></div>
|
<div class="in-progress h-100" [style.width]="runningWidth()"></div>
|
||||||
|
<div class="abort h-100" [style.width]="abortWidth()"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="state-container" [style.width]="totalWidth+'px'">
|
<div class="state-container" [style.width]="totalWidth+'px'">
|
||||||
<div class="clr-row m-0" *ngFor="let item of scanningMetrics?.metrics | keyvalue">
|
<div class="clr-row m-0" *ngFor="let item of scanningMetrics?.metrics | keyvalue">
|
||||||
<div class="state">
|
<div class="state">
|
||||||
<span class="badge" [ngClass]="{ error: isError(item?.key),
|
<span class="badge" [ngClass]="{ error: isError(item?.key),
|
||||||
finished: isFinished(item?.key),
|
finished: isFinished(item?.key),
|
||||||
'in-progress': isInProgress(item?.key)}">{{item?.value}}</span>
|
'in-progress': isInProgress(item?.key),
|
||||||
|
abort: isAborted(item?.key)}">{{item?.value}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="value">{{getI18nKey(item?.key)|translate}}</div>
|
<div class="value">{{getI18nKey(item?.key)|translate}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,6 +103,9 @@
|
|||||||
.in-progress {
|
.in-progress {
|
||||||
background-color: #0079b8;
|
background-color: #0079b8;
|
||||||
}
|
}
|
||||||
|
.abort {
|
||||||
|
background-color: #cccc15;
|
||||||
|
}
|
||||||
.badge {
|
.badge {
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { ScanAllRepoService } from "./scanAll.service";
|
|||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
||||||
import { IServiceConfig, SERVICE_CONFIG } from "../../../entities/service.config";
|
import { IServiceConfig, SERVICE_CONFIG } from "../../../entities/service.config";
|
||||||
import { ScanningMetrics } from "../config";
|
import { ScanningMetrics, Triggers } from "../config";
|
||||||
import { SharedModule } from "../../../utils/shared/shared.module";
|
import { SharedModule } from "../../../utils/shared/shared.module";
|
||||||
import { ErrorHandler } from "../../../utils/error-handler";
|
import { ErrorHandler } from "../../../utils/error-handler";
|
||||||
import { CURRENT_BASE_HREF } from "../../../utils/utils";
|
import { CURRENT_BASE_HREF } from "../../../utils/utils";
|
||||||
@ -23,17 +23,8 @@ let mockedScheduledMetrics: ScanningMetrics = {
|
|||||||
"Success": 20,
|
"Success": 20,
|
||||||
"Error": 30,
|
"Error": 30,
|
||||||
},
|
},
|
||||||
ongoing: false
|
ongoing: false,
|
||||||
};
|
trigger: Triggers.SCHEDULE
|
||||||
let mockedManualMetrics: ScanningMetrics = {
|
|
||||||
total: 100,
|
|
||||||
completed: 20,
|
|
||||||
metrics: {
|
|
||||||
"Error": 10,
|
|
||||||
"Success": 20,
|
|
||||||
"Running": 70
|
|
||||||
},
|
|
||||||
ongoing: true
|
|
||||||
};
|
};
|
||||||
const mockedScanner: Scanner = {
|
const mockedScanner: Scanner = {
|
||||||
"uuid": "ca3c27f3-72f3-11ea-9e46-0242ac170004",
|
"uuid": "ca3c27f3-72f3-11ea-9e46-0242ac170004",
|
||||||
@ -52,12 +43,9 @@ let fakedScanAllRepoService = {
|
|||||||
getSchedule() {
|
getSchedule() {
|
||||||
return of(mockedSchedule);
|
return of(mockedSchedule);
|
||||||
},
|
},
|
||||||
getScheduleMetrics() {
|
getMetrics() {
|
||||||
return of(mockedScheduledMetrics);
|
return of(mockedScheduledMetrics);
|
||||||
},
|
},
|
||||||
getManualMetrics() {
|
|
||||||
return of(mockedManualMetrics);
|
|
||||||
},
|
|
||||||
manualScan() {
|
manualScan() {
|
||||||
return of(true);
|
return of(true);
|
||||||
},
|
},
|
||||||
@ -105,10 +93,9 @@ describe('VulnerabilityConfigComponent', () => {
|
|||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
const ele = fixture.nativeElement.querySelector('.finished');
|
const ele = fixture.nativeElement.querySelector('.finished');
|
||||||
expect(ele.style.width).toEqual('40px');
|
expect(ele.style.width).toEqual('80px');
|
||||||
});
|
});
|
||||||
it('should loop scheduled metrics if scheduled scanning is on going', () => {
|
it('should loop metrics if scheduled scanning is on going', () => {
|
||||||
component.scanningMetrics.isScheduled = true;
|
|
||||||
component.scanningMetrics.ongoing = true;
|
component.scanningMetrics.ongoing = true;
|
||||||
expect(component.scanAvailable).toBeFalsy();
|
expect(component.scanAvailable).toBeFalsy();
|
||||||
});
|
});
|
||||||
@ -116,10 +103,10 @@ describe('VulnerabilityConfigComponent', () => {
|
|||||||
const button = fixture.nativeElement.querySelector('#scan-now');
|
const button = fixture.nativeElement.querySelector('#scan-now');
|
||||||
button.click();
|
button.click();
|
||||||
const ele = fixture.nativeElement.querySelector('.finished');
|
const ele = fixture.nativeElement.querySelector('.finished');
|
||||||
expect(ele.style.width).toEqual('40px');
|
expect(ele.style.width).toEqual('80px');
|
||||||
});
|
});
|
||||||
it('should stop looping manual metrics if manual scanning is finished', () => {
|
it('should stop looping manual metrics if manual scanning is finished', () => {
|
||||||
component.scanningMetrics.isScheduled = false;
|
component.scanningMetrics.trigger = Triggers.MANUAL;
|
||||||
component.scanningMetrics.ongoing = false;
|
component.scanningMetrics.ongoing = false;
|
||||||
component.hasDefaultScanner = true;
|
component.hasDefaultScanner = true;
|
||||||
expect(component.scanAvailable).toBeTruthy();
|
expect(component.scanAvailable).toBeTruthy();
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ScanningMetrics } from '../config';
|
import { ScanningMetrics, Triggers } from '../config';
|
||||||
import { ErrorHandler } from '../../../utils/error-handler';
|
import { ErrorHandler } from '../../../utils/error-handler';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { ScanAllRepoService } from './scanAll.service';
|
import { ScanAllRepoService } from './scanAll.service';
|
||||||
import { OriginCron } from '../../../services/interface';
|
import { OriginCron } from '../../../services';
|
||||||
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component";
|
import { CronScheduleComponent } from "../../cron-schedule";
|
||||||
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../utils/utils";
|
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../utils/utils";
|
||||||
import { errorHandler as errorHandFn} from "../../../utils/shared/shared.utils";
|
import { errorHandler as errorHandFn} from "../../../utils/shared/shared.utils";
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
@ -37,8 +37,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
"Success": "CONFIG.SCANNING.STATUS.SUCCESS",
|
"Success": "CONFIG.SCANNING.STATUS.SUCCESS",
|
||||||
"Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED"
|
"Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED"
|
||||||
};
|
};
|
||||||
private _loopScheduleInterval;
|
private _loopInterval;
|
||||||
private _loopManualInterval;
|
|
||||||
updatedTimeStr: string;
|
updatedTimeStr: string;
|
||||||
onGettingUpdatedTimeStr: boolean;
|
onGettingUpdatedTimeStr: boolean;
|
||||||
hasDefaultScanner: boolean = false;
|
hasDefaultScanner: boolean = false;
|
||||||
@ -52,34 +51,19 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
return this._scanningMetrics;
|
return this._scanningMetrics;
|
||||||
}
|
}
|
||||||
set scanningMetrics(metrics: ScanningMetrics) {
|
set scanningMetrics(metrics: ScanningMetrics) {
|
||||||
// start looping scheduled metrics
|
// start looping metrics
|
||||||
if (metrics && metrics.ongoing && metrics.isScheduled) {
|
if (metrics && metrics.ongoing) {
|
||||||
if (!this._loopScheduleInterval) {
|
if (!this._loopInterval) {
|
||||||
this._loopScheduleInterval = setInterval(() => {
|
this._loopInterval = setInterval(() => {
|
||||||
this.getScheduleMetrics();
|
this.getMetrics();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// stop looping scheduled metrics
|
// stop looping metrics
|
||||||
if (metrics && !metrics.ongoing && metrics.isScheduled) {
|
if (metrics && !metrics.ongoing) {
|
||||||
if (this._loopScheduleInterval) {
|
if (this._loopInterval) {
|
||||||
clearInterval(this._loopScheduleInterval);
|
clearInterval(this._loopInterval);
|
||||||
this._loopScheduleInterval = null;
|
this._loopInterval = null;
|
||||||
}
|
|
||||||
}
|
|
||||||
// start looping manual metrics
|
|
||||||
if (metrics && metrics.ongoing && !metrics.isScheduled) {
|
|
||||||
if (!this._loopManualInterval) {
|
|
||||||
this._loopManualInterval = setInterval(() => {
|
|
||||||
this.getManualMetrics();
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stop looping manual metrics
|
|
||||||
if (metrics && !metrics.ongoing && !metrics.isScheduled) {
|
|
||||||
if (this._loopManualInterval) {
|
|
||||||
clearInterval(this._loopManualInterval);
|
|
||||||
this._loopManualInterval = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._scanningMetrics = metrics;
|
this._scanningMetrics = metrics;
|
||||||
@ -117,7 +101,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
scanners.forEach(scanner => {
|
scanners.forEach(scanner => {
|
||||||
if (scanner.is_default) {
|
if (scanner.is_default) {
|
||||||
flag = true;
|
flag = true;
|
||||||
this.initMetrics();
|
this.getMetrics();
|
||||||
this.getSchedule();
|
this.getSchedule();
|
||||||
this.getScannerMetadata(scanner.uuid);
|
this.getScannerMetadata(scanner.uuid);
|
||||||
}
|
}
|
||||||
@ -169,60 +153,25 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (this._loopScheduleInterval) {
|
if (this._loopInterval) {
|
||||||
clearInterval(this._loopScheduleInterval);
|
clearInterval(this._loopInterval);
|
||||||
this._loopScheduleInterval = null;
|
this._loopInterval = null;
|
||||||
}
|
|
||||||
if (this._loopManualInterval) {
|
|
||||||
clearInterval(this._loopManualInterval);
|
|
||||||
this._loopManualInterval = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isOnScanning(): boolean {
|
isOnScanning(): boolean {
|
||||||
return this.scanningMetrics
|
return this.scanningMetrics
|
||||||
&& this.scanningMetrics.ongoing;
|
&& this.scanningMetrics.ongoing;
|
||||||
}
|
}
|
||||||
getScheduleMetrics() {
|
getMetrics() {
|
||||||
this.gettingMetrics = true;
|
this.gettingMetrics = true;
|
||||||
this.scanningService.getScheduleMetrics()
|
this.scanningService.getMetrics()
|
||||||
.pipe(finalize(() => this.gettingMetrics = false))
|
.pipe(finalize(() => this.gettingMetrics = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
if (response) {
|
if (response) {
|
||||||
response.isScheduled = true;
|
|
||||||
this.scanningMetrics = response;
|
this.scanningMetrics = response;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
getManualMetrics() {
|
|
||||||
this.gettingMetrics = true;
|
|
||||||
this.scanningService.getManualMetrics()
|
|
||||||
.pipe(finalize(() => this.gettingMetrics = false))
|
|
||||||
.subscribe(response => {
|
|
||||||
if (response) {
|
|
||||||
response.isScheduled = false;
|
|
||||||
this.scanningMetrics = response;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
initMetrics() {
|
|
||||||
// get scheduled metrics first
|
|
||||||
this.scanningService.getScheduleMetrics()
|
|
||||||
.pipe(finalize(() => this.gettingMetrics = false))
|
|
||||||
.subscribe(response => {
|
|
||||||
// if scheduled scanning is on going
|
|
||||||
if (response && response.ongoing) {
|
|
||||||
response.isScheduled = true;
|
|
||||||
this.scanningMetrics = response;
|
|
||||||
} else {
|
|
||||||
this.getManualMetrics();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
this.errorHandler.error(error);
|
|
||||||
// if error, get manual metrics
|
|
||||||
this.getManualMetrics();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getI18nKey(str: string): string {
|
getI18nKey(str: string): string {
|
||||||
if (str && this.i18nKeyMap[str]) {
|
if (str && this.i18nKeyMap[str]) {
|
||||||
return this.i18nKeyMap[str];
|
return this.i18nKeyMap[str];
|
||||||
@ -259,6 +208,16 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
abortWidth() {
|
||||||
|
if (this.scanningMetrics
|
||||||
|
&& this.scanningMetrics.metrics
|
||||||
|
&& this.scanningMetrics.total
|
||||||
|
&& this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED]) {
|
||||||
|
return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED] /
|
||||||
|
this.scanningMetrics.total * this.totalWidth + 'px';
|
||||||
|
}
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
scanNow(): void {
|
scanNow(): void {
|
||||||
if (this.onSubmitting) {
|
if (this.onSubmitting) {
|
||||||
return; // Aoid duplicated submitting
|
return; // Aoid duplicated submitting
|
||||||
@ -277,7 +236,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
// reset metrics and then get new metrics
|
// reset metrics and then get new metrics
|
||||||
this.scanningMetrics = null;
|
this.scanningMetrics = null;
|
||||||
this.getManualMetrics();
|
this.getMetrics();
|
||||||
}
|
}
|
||||||
, error => {
|
, error => {
|
||||||
if (error && error.status && error.status === 412) {
|
if (error && error.status && error.status === 412) {
|
||||||
@ -341,4 +300,13 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
isInProgress(status: string): boolean {
|
isInProgress(status: string): boolean {
|
||||||
return status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
return status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
||||||
}
|
}
|
||||||
|
isAborted(status: string): boolean {
|
||||||
|
return status === VULNERABILITY_SCAN_STATUS.STOPPED;
|
||||||
|
}
|
||||||
|
isManual() {
|
||||||
|
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.MANUAL;
|
||||||
|
}
|
||||||
|
isSchedule() {
|
||||||
|
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.SCHEDULE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user