mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-24 19:42:52 +02:00
Add stop scan functionality (#15528)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
c5003f38ba
commit
9e9c4a03bb
@ -13,10 +13,10 @@
|
|||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-2 flex-200">
|
<div class="clr-col-2 flex-200">
|
||||||
<div class="btn-scan-right btn-scan margin-top-16px">
|
<div class="btn-scan-right btn-scan margin-top-16px">
|
||||||
<button id="scan-now" class="btn btn-primary btn-scan" (click)="scanNow()"
|
<button id="scan" class="btn btn-primary btn-scan" (click)="scanOrStop()"
|
||||||
[disabled]="!scanAvailable">
|
[disabled]="onSubmitting || !hasDefaultScanner">
|
||||||
<span *ngIf="scanAvailable">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</span>
|
<span *ngIf="!isOnScanning()">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</span>
|
||||||
<span *ngIf="!scanAvailable">{{ 'CONFIG.SCANNING.SCAN' | translate }}</span>
|
<span *ngIf="isOnScanning()">{{ 'VULNERABILITY.STOP_NOW' | translate }}</span>
|
||||||
</button>
|
</button>
|
||||||
<span [hidden]="!isOnScanning()" class="spinner spinner-inline margin-left-5 v-mid"></span>
|
<span [hidden]="!isOnScanning()" class="spinner spinner-inline margin-left-5 v-mid"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,9 +4,10 @@ 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 { ScanningMetrics, Triggers } from "../../config/config";
|
import { ScanningMetrics, Triggers } from "../../config/config";
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
|
||||||
import { Scanner } from "../scanner/scanner";
|
import { Scanner } from "../scanner/scanner";
|
||||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
|
import { delay } from "rxjs/operators";
|
||||||
|
import { ScanAllService } from "../../../../../../ng-swagger-gen/services/scan-all.service";
|
||||||
|
|
||||||
let component: VulnerabilityConfigComponent;
|
let component: VulnerabilityConfigComponent;
|
||||||
let fixture: ComponentFixture<VulnerabilityConfigComponent>;
|
let fixture: ComponentFixture<VulnerabilityConfigComponent>;
|
||||||
@ -51,9 +52,10 @@ let fakedScanAllRepoService = {
|
|||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fakedErrorHandler = {
|
|
||||||
info() {
|
const fakedScanAllService = {
|
||||||
return null;
|
stopScanAll() {
|
||||||
|
return of(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,8 +72,8 @@ describe('VulnerabilityConfigComponent', () => {
|
|||||||
VulnerabilityConfigComponent
|
VulnerabilityConfigComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: ErrorHandler, useValue: fakedErrorHandler},
|
|
||||||
{provide: ScanAllRepoService, useValue: fakedScanAllRepoService},
|
{provide: ScanAllRepoService, useValue: fakedScanAllRepoService},
|
||||||
|
{provide: ScanAllService, useValue: fakedScanAllService},
|
||||||
// open auto detect
|
// open auto detect
|
||||||
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
||||||
]
|
]
|
||||||
@ -94,7 +96,7 @@ describe('VulnerabilityConfigComponent', () => {
|
|||||||
expect(component.scanAvailable).toBeFalsy();
|
expect(component.scanAvailable).toBeFalsy();
|
||||||
});
|
});
|
||||||
it('will trigger scan now and get manual metrics', () => {
|
it('will trigger scan now and get manual metrics', () => {
|
||||||
const button = fixture.nativeElement.querySelector('#scan-now');
|
const button = fixture.nativeElement.querySelector('#scan');
|
||||||
button.click();
|
button.click();
|
||||||
const ele = fixture.nativeElement.querySelector('.finished');
|
const ele = fixture.nativeElement.querySelector('.finished');
|
||||||
expect(ele.style.width).toEqual('80px');
|
expect(ele.style.width).toEqual('80px');
|
||||||
@ -104,5 +106,11 @@ describe('VulnerabilityConfigComponent', () => {
|
|||||||
component.scanningMetrics.ongoing = false;
|
component.scanningMetrics.ongoing = false;
|
||||||
component.hasDefaultScanner = true;
|
component.hasDefaultScanner = true;
|
||||||
expect(component.scanAvailable).toBeTruthy();
|
expect(component.scanAvailable).toBeTruthy();
|
||||||
|
const spnManual: HTMLSpanElement = fixture.nativeElement.querySelector(".float-left");
|
||||||
|
expect(spnManual.innerText).toEqual('CONFIG.SCANNING.SCHEDULED');
|
||||||
|
const scanBtn: HTMLButtonElement = fixture.nativeElement.querySelector("#scan");
|
||||||
|
scanBtn.click();
|
||||||
|
const spnSchedule: HTMLSpanElement = fixture.nativeElement.querySelector(".float-left");
|
||||||
|
expect(spnSchedule.innerText).toEqual('CONFIG.SCANNING.MANUAL');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ScanningMetrics, Triggers } from '../../config/config';
|
import { ScanningMetrics, Triggers } from '../../config/config';
|
||||||
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
||||||
@ -9,9 +9,11 @@ import { CronScheduleComponent } from "../../../../shared/components/cron-schedu
|
|||||||
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../../shared/units/utils";
|
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../../shared/units/utils";
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { errorHandler } from "../../../../shared/units/shared.utils";
|
import { errorHandler } from "../../../../shared/units/shared.utils";
|
||||||
|
import { ScanAllService } from "../../../../../../ng-swagger-gen/services/scan-all.service";
|
||||||
|
|
||||||
const SCHEDULE_TYPE_NONE = "None";
|
const SCHEDULE_TYPE_NONE = "None";
|
||||||
const TIMEOUT: number = 5000;
|
const TIMEOUT: number = 5000;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'vulnerability-config',
|
selector: 'vulnerability-config',
|
||||||
templateUrl: './vulnerability-config.component.html',
|
templateUrl: './vulnerability-config.component.html',
|
||||||
@ -42,18 +44,23 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
onGettingUpdatedTimeStr: boolean;
|
onGettingUpdatedTimeStr: boolean;
|
||||||
hasDefaultScanner: boolean = false;
|
hasDefaultScanner: boolean = false;
|
||||||
private _timeout: any;
|
private _timeout: any;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private scanningService: ScanAllRepoService,
|
private scanningService: ScanAllRepoService,
|
||||||
|
private newScanAllService: ScanAllService,
|
||||||
private errorHandlerEntity: ErrorHandler,
|
private errorHandlerEntity: ErrorHandler,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
) { }
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
get scanningMetrics(): ScanningMetrics {
|
get scanningMetrics(): ScanningMetrics {
|
||||||
return this._scanningMetrics;
|
return this._scanningMetrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
set scanningMetrics(metrics: ScanningMetrics) {
|
set scanningMetrics(metrics: ScanningMetrics) {
|
||||||
this._scanningMetrics = metrics;
|
this._scanningMetrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
get scanAvailable(): boolean {
|
get scanAvailable(): boolean {
|
||||||
return !this.onSubmitting
|
return !this.onSubmitting
|
||||||
&& !this.gettingMetrics
|
&& !this.gettingMetrics
|
||||||
@ -66,6 +73,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
this.getLabelCurrent = res;
|
this.getLabelCurrent = res;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSchedule() {
|
getSchedule() {
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.scanningService.getSchedule()
|
this.scanningService.getSchedule()
|
||||||
@ -78,6 +86,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
this.errorHandlerEntity.error(error);
|
this.errorHandlerEntity.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getScanners() {
|
getScanners() {
|
||||||
this.onGettingUpdatedTimeStr = true;
|
this.onGettingUpdatedTimeStr = true;
|
||||||
this.scanningService.getScanners()
|
this.scanningService.getScanners()
|
||||||
@ -108,6 +117,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
this.onGettingUpdatedTimeStr = false;
|
this.onGettingUpdatedTimeStr = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getScannerMetadata(uid: string) {
|
getScannerMetadata(uid: string) {
|
||||||
this.scanningService.getScannerMetadata(uid)
|
this.scanningService.getScannerMetadata(uid)
|
||||||
.pipe(finalize(() => this.onGettingUpdatedTimeStr = false))
|
.pipe(finalize(() => this.onGettingUpdatedTimeStr = false))
|
||||||
@ -133,6 +143,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getScanText();
|
this.getScanText();
|
||||||
this.getScanners();
|
this.getScanners();
|
||||||
@ -144,10 +155,12 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
this._timeout = null;
|
this._timeout = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isOnScanning(): boolean {
|
isOnScanning(): boolean {
|
||||||
return this.scanningMetrics
|
return this.scanningMetrics
|
||||||
&& this.scanningMetrics.ongoing;
|
&& this.scanningMetrics.ongoing;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMetrics() {
|
getMetrics() {
|
||||||
this.gettingMetrics = true;
|
this.gettingMetrics = true;
|
||||||
this.scanningService.getMetrics()
|
this.scanningService.getMetrics()
|
||||||
@ -169,12 +182,14 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorWidth() {
|
errorWidth() {
|
||||||
if (this.scanningMetrics
|
if (this.scanningMetrics
|
||||||
&& this.scanningMetrics.metrics
|
&& this.scanningMetrics.metrics
|
||||||
@ -185,6 +200,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
finishedWidth() {
|
finishedWidth() {
|
||||||
if (this.scanningMetrics
|
if (this.scanningMetrics
|
||||||
&& this.scanningMetrics.metrics
|
&& this.scanningMetrics.metrics
|
||||||
@ -195,6 +211,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
runningWidth() {
|
runningWidth() {
|
||||||
if (this.scanningMetrics
|
if (this.scanningMetrics
|
||||||
&& this.scanningMetrics.metrics
|
&& this.scanningMetrics.metrics
|
||||||
@ -205,6 +222,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
abortWidth() {
|
abortWidth() {
|
||||||
if (this.scanningMetrics
|
if (this.scanningMetrics
|
||||||
&& this.scanningMetrics.metrics
|
&& this.scanningMetrics.metrics
|
||||||
@ -215,6 +233,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return '0';
|
return '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
scanNow(): void {
|
scanNow(): void {
|
||||||
if (this.onSubmitting) {
|
if (this.onSubmitting) {
|
||||||
return; // Aoid duplicated submitting
|
return; // Aoid duplicated submitting
|
||||||
@ -288,22 +307,47 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isError(status: string): boolean {
|
isError(status: string): boolean {
|
||||||
return status === VULNERABILITY_SCAN_STATUS.ERROR;
|
return status === VULNERABILITY_SCAN_STATUS.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFinished(status: string): boolean {
|
isFinished(status: string): boolean {
|
||||||
return status === VULNERABILITY_SCAN_STATUS.SUCCESS;
|
return status === VULNERABILITY_SCAN_STATUS.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
isInProgress(status: string): boolean {
|
isInProgress(status: string): boolean {
|
||||||
return status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
return status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
isAborted(status: string): boolean {
|
isAborted(status: string): boolean {
|
||||||
return status === VULNERABILITY_SCAN_STATUS.STOPPED;
|
return status === VULNERABILITY_SCAN_STATUS.STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
isManual() {
|
isManual() {
|
||||||
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.MANUAL;
|
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.MANUAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSchedule() {
|
isSchedule() {
|
||||||
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.SCHEDULE;
|
return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.SCHEDULE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scanOrStop() {
|
||||||
|
if (this.isOnScanning()) {
|
||||||
|
this.stopNow();
|
||||||
|
} else {
|
||||||
|
this.scanNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stopNow() {
|
||||||
|
this.onSubmitting = true;
|
||||||
|
this.newScanAllService.stopScanAll()
|
||||||
|
.pipe(finalize(() => this.onSubmitting = false))
|
||||||
|
.subscribe(res => {
|
||||||
|
this.errorHandlerEntity.info('CONFIG.SCANNING.STOP_SCAN_ALL_SUCCESS');
|
||||||
|
}, error => {
|
||||||
|
this.errorHandlerEntity.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,11 @@ export const INITIAL_ACCESSES: FrontAccess[] = [
|
|||||||
"resource": "scan",
|
"resource": "scan",
|
||||||
"action": "create",
|
"action": "create",
|
||||||
"checked": true
|
"checked": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"resource": "scan",
|
||||||
|
"action": "stop",
|
||||||
|
"checked": true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -108,7 +113,8 @@ export const ACTION_RESOURCE_I18N_MAP = {
|
|||||||
'tag': 'REPLICATION.TAG',
|
'tag': 'REPLICATION.TAG',
|
||||||
'artifact-label': 'SYSTEM_ROBOT.ARTIFACT_LABEL',
|
'artifact-label': 'SYSTEM_ROBOT.ARTIFACT_LABEL',
|
||||||
'scan': 'SYSTEM_ROBOT.SCAN',
|
'scan': 'SYSTEM_ROBOT.SCAN',
|
||||||
'scanner-pull': 'SYSTEM_ROBOT.SCANNER_PULL'
|
'scanner-pull': 'SYSTEM_ROBOT.SCANNER_PULL',
|
||||||
|
'stop': 'SYSTEM_ROBOT.STOP'
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum ExpirationType {
|
export enum ExpirationType {
|
||||||
|
@ -11,14 +11,16 @@
|
|||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<div class="clr-row center">
|
<div class="clr-row center">
|
||||||
<div class="ml-05">
|
<div class="ml-05">
|
||||||
<button id="scan-btn" (click)="scanNow()" type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!(hasEnabledScanner && hasScanningPermission && !onSendingScanCommand)">
|
<button id="scan-btn" (click)="scanOrStop()" type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!canScan()">
|
||||||
<clr-icon shape="shield-check" size="16"></clr-icon>
|
<clr-icon shape="shield-check" size="16" *ngIf="!isRunningState()"></clr-icon>
|
||||||
<span>{{'VULNERABILITY.SCAN_NOW' | translate}}</span>
|
<clr-icon shape="stop" size="16" *ngIf="isRunningState()"></clr-icon>
|
||||||
|
<span *ngIf="!isRunningState()">{{'VULNERABILITY.SCAN_NOW' | translate}}</span>
|
||||||
|
<span *ngIf="isRunningState()">{{'VULNERABILITY.STOP_NOW' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-1">
|
<div class="ml-1">
|
||||||
<div [hidden]="!shouldShowBar()">
|
<div [hidden]="!shouldShowBar()">
|
||||||
<hbr-vulnerability-bar [summary]="handleScanOverview(artifact?.scan_overview)" [inputScanner]="scanner"
|
<hbr-vulnerability-bar (submitStopFinish)="submitStopFinish($event)" [summary]="handleScanOverview(artifact?.scan_overview)" [inputScanner]="scanner"
|
||||||
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
||||||
[artifactDigest]="digest">
|
[artifactDigest]="digest">
|
||||||
</hbr-vulnerability-bar>
|
</hbr-vulnerability-bar>
|
||||||
|
@ -15,7 +15,6 @@ import {
|
|||||||
} from "../../../../../../shared/services";
|
} from "../../../../../../shared/services";
|
||||||
import { AdditionLink } from "../../../../../../../../ng-swagger-gen/models/addition-link";
|
import { AdditionLink } from "../../../../../../../../ng-swagger-gen/models/addition-link";
|
||||||
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
||||||
import { ChannelService } from "../../../../../../shared/services/channel.service";
|
|
||||||
import { SessionService } from "../../../../../../shared/services/session.service";
|
import { SessionService } from "../../../../../../shared/services/session.service";
|
||||||
import { SessionUser } from "../../../../../../shared/entities/session-user";
|
import { SessionUser } from "../../../../../../shared/entities/session-user";
|
||||||
import { delay } from "rxjs/operators";
|
import { delay } from "rxjs/operators";
|
||||||
@ -66,13 +65,6 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
return of(true);
|
return of(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const fakedChannelService = {
|
|
||||||
ArtifactDetail$: {
|
|
||||||
subscribe() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const mockedUser: SessionUser = {
|
const mockedUser: SessionUser = {
|
||||||
user_id: 1,
|
user_id: 1,
|
||||||
username: 'admin',
|
username: 'admin',
|
||||||
@ -120,7 +112,6 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
{provide: AdditionsService, useValue: fakedAdditionsService},
|
{provide: AdditionsService, useValue: fakedAdditionsService},
|
||||||
{provide: UserPermissionService, useValue: fakedUserPermissionService},
|
{provide: UserPermissionService, useValue: fakedUserPermissionService},
|
||||||
{provide: ScanningResultService, useValue: fakedScanningResultService},
|
{provide: ScanningResultService, useValue: fakedScanningResultService},
|
||||||
{provide: ChannelService, useValue: fakedChannelService},
|
|
||||||
{provide: SessionService, useValue: fakedSessionService},
|
{provide: SessionService, useValue: fakedSessionService},
|
||||||
{provide: ProjectService, useValue: fakedProjectService},
|
{provide: ProjectService, useValue: fakedProjectService},
|
||||||
],
|
],
|
||||||
@ -160,4 +151,10 @@ describe('ArtifactVulnerabilitiesComponent', () => {
|
|||||||
const cells = firstRow.querySelectorAll("clr-dg-cell");
|
const cells = firstRow.querySelectorAll("clr-dg-cell");
|
||||||
expect(cells[cells.length - 1].innerText).toEqual("TAG_RETENTION.YES");
|
expect(cells[cells.length - 1].innerText).toEqual("TAG_RETENTION.YES");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("scan button should show the right text", async () => {
|
||||||
|
fixture.autoDetectChanges(true);
|
||||||
|
const scanBtn: HTMLButtonElement = fixture.nativeElement.querySelector("#scan-btn");
|
||||||
|
expect(scanBtn.innerText).toContain("VULNERABILITY.SCAN_NOW");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -14,11 +14,11 @@ import {
|
|||||||
} from "../../../../../../shared/services";
|
} from "../../../../../../shared/services";
|
||||||
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
||||||
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../shared/units/utils";
|
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../shared/units/utils";
|
||||||
import { ChannelService } from "../../../../../../shared/services/channel.service";
|
|
||||||
import { ResultBarChartComponent } from "../../vulnerability-scanning/result-bar-chart.component";
|
import { ResultBarChartComponent } from "../../vulnerability-scanning/result-bar-chart.component";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { Artifact } from "../../../../../../../../ng-swagger-gen/models/artifact";
|
import { Artifact } from "../../../../../../../../ng-swagger-gen/models/artifact";
|
||||||
import { SessionService } from "../../../../../../shared/services/session.service";
|
import { SessionService } from "../../../../../../shared/services/session.service";
|
||||||
|
import { EventService, HarborEvent } from "../../../../../../services/event-service/event.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-artifact-vulnerabilities',
|
selector: 'hbr-artifact-vulnerabilities',
|
||||||
@ -49,6 +49,7 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
cvssSort: ClrDatagridComparatorInterface<VulnerabilityItem>;
|
||||||
hasScanningPermission: boolean = false;
|
hasScanningPermission: boolean = false;
|
||||||
onSendingScanCommand: boolean = false;
|
onSendingScanCommand: boolean = false;
|
||||||
|
onSendingStopCommand: boolean = false;
|
||||||
hasShowLoading: boolean = false;
|
hasShowLoading: boolean = false;
|
||||||
@ViewChild(ResultBarChartComponent)
|
@ViewChild(ResultBarChartComponent)
|
||||||
resultBarChartComponent: ResultBarChartComponent;
|
resultBarChartComponent: ResultBarChartComponent;
|
||||||
@ -60,7 +61,7 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
private additionsService: AdditionsService,
|
private additionsService: AdditionsService,
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private channel: ChannelService,
|
private eventService: EventService,
|
||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private systemInfoService: SystemInfoService,
|
private systemInfoService: SystemInfoService,
|
||||||
@ -86,8 +87,10 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
this.getScanningPermission();
|
this.getScanningPermission();
|
||||||
this.getProjectScanner();
|
this.getProjectScanner();
|
||||||
if (!this.sub) {
|
if (!this.sub) {
|
||||||
this.sub = this.channel.ArtifactDetail$.subscribe(tag => {
|
this.sub = this.eventService.subscribe(HarborEvent.UPDATE_VULNERABILITY_INFO, (artifact: Artifact) => {
|
||||||
|
if (artifact?.digest === this.artifact?.digest) {
|
||||||
this.getVulnerabilities();
|
this.getVulnerabilities();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -190,20 +193,27 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
scanNow() {
|
scanNow() {
|
||||||
this.onSendingScanCommand = true;
|
this.onSendingScanCommand = true;
|
||||||
this.channel.publishScanEvent(this.repoName + "/" + this.digest);
|
this.eventService.publish(HarborEvent.START_SCAN_ARTIFACT, this.repoName + "/" + this.digest);
|
||||||
}
|
}
|
||||||
submitFinish(e: boolean) {
|
submitFinish(e: boolean) {
|
||||||
this.onSendingScanCommand = e;
|
this.onSendingScanCommand = e;
|
||||||
}
|
}
|
||||||
|
submitStopFinish(e: boolean) {
|
||||||
|
this.onSendingStopCommand = e;
|
||||||
|
}
|
||||||
shouldShowBar(): boolean {
|
shouldShowBar(): boolean {
|
||||||
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
||||||
&& (this.resultBarChartComponent.queued || this.resultBarChartComponent.scanning || this.resultBarChartComponent.error);
|
&& (this.resultBarChartComponent.queued
|
||||||
|
|| this.resultBarChartComponent.scanning
|
||||||
|
|| this.resultBarChartComponent.error
|
||||||
|
|| this.resultBarChartComponent.stopped);
|
||||||
}
|
}
|
||||||
hasScanned(): boolean {
|
hasScanned(): boolean {
|
||||||
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
||||||
&& !(this.resultBarChartComponent.completed
|
&& !(this.resultBarChartComponent.completed
|
||||||
|| this.resultBarChartComponent.error
|
|| this.resultBarChartComponent.error
|
||||||
|| this.resultBarChartComponent.queued
|
|| this.resultBarChartComponent.queued
|
||||||
|
|| this.resultBarChartComponent.stopped
|
||||||
|| this.resultBarChartComponent.scanning);
|
|| this.resultBarChartComponent.scanning);
|
||||||
}
|
}
|
||||||
handleScanOverview(scanOverview: any): any {
|
handleScanOverview(scanOverview: any): any {
|
||||||
@ -256,4 +266,23 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
isRunningState(): boolean {
|
||||||
|
return this.hasViewInitWithDelay && this.resultBarChartComponent
|
||||||
|
&& (this.resultBarChartComponent.queued || this.resultBarChartComponent.scanning);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanOrStop() {
|
||||||
|
if (this.isRunningState()) {
|
||||||
|
this.stopNow();
|
||||||
|
} else {
|
||||||
|
this.scanNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stopNow() {
|
||||||
|
this.onSendingStopCommand = true;
|
||||||
|
this.eventService.publish(HarborEvent.STOP_SCAN_ARTIFACT, this.repoName + "/" + this.digest);
|
||||||
|
}
|
||||||
|
canScan(): boolean {
|
||||||
|
return this.hasEnabledScanner && this.hasScanningPermission && !this.onSendingScanCommand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,12 @@
|
|||||||
<span>{{'VULNERABILITY.SCAN_NOW' | translate}}</span>
|
<span>{{'VULNERABILITY.SCAN_NOW' | translate}}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<button id="stop-scan" [clrLoading]="stopBtnState" type="button" class="btn btn-secondary scan-btn"
|
||||||
|
[disabled]="!(canStopScan() && hasScanImagePermission )" (click)="stopNow()">
|
||||||
|
<clr-icon shape="stop" size="16"></clr-icon>
|
||||||
|
<span>{{'VULNERABILITY.STOP_NOW' | translate}}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<clr-dropdown class="btn btn-link" *ngIf="!depth">
|
<clr-dropdown class="btn btn-link" *ngIf="!depth">
|
||||||
<span clrDropdownTrigger id="artifact-list-action" class="btn pl-0">
|
<span clrDropdownTrigger id="artifact-list-action" class="btn pl-0">
|
||||||
{{'BUTTON.ACTIONS' | translate}}
|
{{'BUTTON.ACTIONS' | translate}}
|
||||||
@ -257,7 +263,7 @@
|
|||||||
<span *ngIf="!hasVul(artifact)">
|
<span *ngIf="!hasVul(artifact)">
|
||||||
{{'ARTIFACT.SCAN_UNSUPPORTED' | translate}}
|
{{'ARTIFACT.SCAN_UNSUPPORTED' | translate}}
|
||||||
</span>
|
</span>
|
||||||
<hbr-vulnerability-bar (scanFinished)="scanFinished($event)" *ngIf="hasVul(artifact)" [inputScanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
<hbr-vulnerability-bar (submitStopFinish)="submitStopFinish($event)" (scanFinished)="scanFinished($event)" *ngIf="hasVul(artifact)" [inputScanner]="handleScanOverview(artifact.scan_overview)?.scanner"
|
||||||
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
|
||||||
[artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)">
|
[artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)">
|
||||||
</hbr-vulnerability-bar>
|
</hbr-vulnerability-bar>
|
||||||
|
@ -22,7 +22,6 @@ import { ConfirmationDialogComponent } from "../../../../../../../shared/compone
|
|||||||
import { ImageNameInputComponent } from "../../../../../../../shared/components/image-name-input/image-name-input.component";
|
import { ImageNameInputComponent } from "../../../../../../../shared/components/image-name-input/image-name-input.component";
|
||||||
import { CopyInputComponent } from "../../../../../../../shared/components/push-image/copy-input.component";
|
import { CopyInputComponent } from "../../../../../../../shared/components/push-image/copy-input.component";
|
||||||
import { ErrorHandler } from "../../../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../../../shared/units/error-handler";
|
||||||
import { ChannelService } from "../../../../../../../shared/services/channel.service";
|
|
||||||
import { OperationService } from "../../../../../../../shared/components/operation/operation.service";
|
import { OperationService } from "../../../../../../../shared/components/operation/operation.service";
|
||||||
import { ArtifactService as NewArtifactService } from "../../../../../../../../../ng-swagger-gen/services/artifact.service";
|
import { ArtifactService as NewArtifactService } from "../../../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
import { Tag } from "../../../../../../../../../ng-swagger-gen/models/tag";
|
import { Tag } from "../../../../../../../../../ng-swagger-gen/models/tag";
|
||||||
@ -307,8 +306,6 @@ describe("ArtifactListTabComponent (inline template)", () => {
|
|||||||
CopyInputComponent
|
CopyInputComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
|
||||||
ChannelService,
|
|
||||||
ArtifactDefaultService,
|
ArtifactDefaultService,
|
||||||
{ provide: Router, useValue: mockRouter },
|
{ provide: Router, useValue: mockRouter },
|
||||||
{ provide: ArtifactService, useValue: mockNewArtifactService },
|
{ provide: ArtifactService, useValue: mockNewArtifactService },
|
||||||
|
@ -36,7 +36,6 @@ import { CopyInputComponent } from "../../../../../../../shared/components/push-
|
|||||||
import { ErrorHandler } from "../../../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../../../shared/units/error-handler";
|
||||||
import { ArtifactService } from "../../../artifact.service";
|
import { ArtifactService } from "../../../artifact.service";
|
||||||
import { OperationService } from "../../../../../../../shared/components/operation/operation.service";
|
import { OperationService } from "../../../../../../../shared/components/operation/operation.service";
|
||||||
import { ChannelService } from "../../../../../../../shared/services/channel.service";
|
|
||||||
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../../../../shared/entities/shared.const";
|
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../../../../shared/entities/shared.const";
|
||||||
import { operateChanges, OperateInfo, OperationState } from "../../../../../../../shared/components/operation/operate";
|
import { operateChanges, OperateInfo, OperationState } from "../../../../../../../shared/components/operation/operate";
|
||||||
import { artifactDefault, ArtifactFront as Artifact, ArtifactFront, artifactPullCommands, mutipleFilter } from '../../../artifact';
|
import { artifactDefault, ArtifactFront as Artifact, ArtifactFront, artifactPullCommands, mutipleFilter } from '../../../artifact';
|
||||||
@ -52,6 +51,7 @@ import { ConfirmationAcknowledgement } from "../../../../../../global-confirmati
|
|||||||
import { UN_LOGGED_PARAM } from "../../../../../../../account/sign-in/sign-in.service";
|
import { UN_LOGGED_PARAM } from "../../../../../../../account/sign-in/sign-in.service";
|
||||||
import { Label } from "../../../../../../../../../ng-swagger-gen/models/label";
|
import { Label } from "../../../../../../../../../ng-swagger-gen/models/label";
|
||||||
import { LabelService } from "../../../../../../../../../ng-swagger-gen/services/label.service";
|
import { LabelService } from "../../../../../../../../../ng-swagger-gen/services/label.service";
|
||||||
|
import { EventService, HarborEvent } from "../../../../../../../services/event-service/event.service";
|
||||||
|
|
||||||
export interface LabelState {
|
export interface LabelState {
|
||||||
iconsShow: boolean;
|
iconsShow: boolean;
|
||||||
@ -142,6 +142,9 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
hasEnabledScanner: boolean;
|
hasEnabledScanner: boolean;
|
||||||
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
onSendingScanCommand: boolean;
|
onSendingScanCommand: boolean;
|
||||||
|
onSendingStopScanCommand: boolean = false;
|
||||||
|
onStopScanArtifactsLength: number = 0;
|
||||||
|
scanStoppedArtifactLength: number = 0;
|
||||||
|
|
||||||
artifactDigest: string;
|
artifactDigest: string;
|
||||||
depth: string;
|
depth: string;
|
||||||
@ -157,6 +160,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
scanFinishedArtifactLength: number = 0;
|
scanFinishedArtifactLength: number = 0;
|
||||||
onScanArtifactsLength: number = 0;
|
onScanArtifactsLength: number = 0;
|
||||||
|
stopBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||||
|
updateArtifactSub: Subscription;
|
||||||
constructor(
|
constructor(
|
||||||
private errorHandlerService: ErrorHandler,
|
private errorHandlerService: ErrorHandler,
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
@ -165,7 +170,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
private newArtifactService: NewArtifactService,
|
private newArtifactService: NewArtifactService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
private operationService: OperationService,
|
private operationService: OperationService,
|
||||||
private channel: ChannelService,
|
private eventService: EventService,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
@ -186,6 +191,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.init();
|
this.init();
|
||||||
});
|
});
|
||||||
|
if (!this.updateArtifactSub) {
|
||||||
|
this.updateArtifactSub = this.eventService.subscribe(HarborEvent.UPDATE_VULNERABILITY_INFO, (artifact: Artifact) => {
|
||||||
|
if (this.artifactList && this.artifactList.length) {
|
||||||
|
this.artifactList.forEach(item => {
|
||||||
|
if (item.digest === artifact.digest) {
|
||||||
|
item.scan_overview = artifact.scan_overview;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
if (this.triggerSub) {
|
if (this.triggerSub) {
|
||||||
@ -200,6 +216,10 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
this.stickLabelNameFilterSub.unsubscribe();
|
this.stickLabelNameFilterSub.unsubscribe();
|
||||||
this.stickLabelNameFilterSub = null;
|
this.stickLabelNameFilterSub = null;
|
||||||
}
|
}
|
||||||
|
if (this.updateArtifactSub) {
|
||||||
|
this.updateArtifactSub.unsubscribe();
|
||||||
|
this.updateArtifactSub = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
init() {
|
init() {
|
||||||
this.hasInit = true;
|
this.hasInit = true;
|
||||||
@ -921,12 +941,21 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
|
return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
|
||||||
}
|
}
|
||||||
// Whether show the 'scan now' menu
|
// if has running job, return false
|
||||||
canScanNow(): boolean {
|
canScanNow(): boolean {
|
||||||
if (!this.hasScanImagePermission) { return false; }
|
if (!this.hasScanImagePermission) { return false; }
|
||||||
if (this.onSendingScanCommand) { return false; }
|
if (this.onSendingScanCommand) { return false; }
|
||||||
let st: string = this.scanStatus(this.selectedRow[0]);
|
if (this.selectedRow && this.selectedRow.length) {
|
||||||
return st !== VULNERABILITY_SCAN_STATUS.RUNNING;
|
let flag: boolean = true;
|
||||||
|
this.selectedRow.forEach(item => {
|
||||||
|
const st: string = this.scanStatus(item);
|
||||||
|
if (this.isRunningState(st)) {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
getImagePermissionRule(projectId: number): void {
|
getImagePermissionRule(projectId: number): void {
|
||||||
const permissions = [
|
const permissions = [
|
||||||
@ -958,7 +987,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
this.onSendingScanCommand = true;
|
this.onSendingScanCommand = true;
|
||||||
this.selectedRow.forEach((data: any) => {
|
this.selectedRow.forEach((data: any) => {
|
||||||
let digest = data.digest;
|
let digest = data.digest;
|
||||||
this.channel.publishScanEvent(this.repoName + "/" + digest);
|
this.eventService.publish(HarborEvent.START_SCAN_ARTIFACT, this.repoName + "/" + digest);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
selectedRowHasVul(): boolean {
|
selectedRowHasVul(): boolean {
|
||||||
@ -972,11 +1001,18 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
submitFinish(e: boolean) {
|
submitFinish(e: boolean) {
|
||||||
this.scanFinishedArtifactLength += 1;
|
this.scanFinishedArtifactLength += 1;
|
||||||
// all selected scan action has start
|
// all selected scan action has started
|
||||||
if (this.scanFinishedArtifactLength === this.onScanArtifactsLength) {
|
if (this.scanFinishedArtifactLength === this.onScanArtifactsLength) {
|
||||||
this.onSendingScanCommand = e;
|
this.onSendingScanCommand = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
submitStopFinish(e: boolean) {
|
||||||
|
this.scanStoppedArtifactLength += 1;
|
||||||
|
// all selected scan action has stopped
|
||||||
|
if (this.scanStoppedArtifactLength === this.onStopScanArtifactsLength) {
|
||||||
|
this.onSendingScanCommand = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
// pull command
|
// pull command
|
||||||
onCpError($event: any): void {
|
onCpError($event: any): void {
|
||||||
this.copyInput.setPullCommendShow();
|
this.copyInput.setPullCommendShow();
|
||||||
@ -1108,7 +1144,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
if (res.headers) {
|
if (res.headers) {
|
||||||
let xHeader: string = res.headers.get("x-total-count");
|
let xHeader: string = res.headers.get("x-total-count");
|
||||||
if (xHeader) {
|
if (xHeader) {
|
||||||
item.tagNumber = Number.parseInt(xHeader);
|
item.tagNumber = Number.parseInt(xHeader, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.tags = res.body;
|
item.tags = res.body;
|
||||||
@ -1117,4 +1153,35 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return true if all selected rows are in "running" state
|
||||||
|
canStopScan(): boolean {
|
||||||
|
if (this.onSendingStopScanCommand) { return false; }
|
||||||
|
if (this.selectedRow && this.selectedRow.length) {
|
||||||
|
let flag: boolean = true;
|
||||||
|
this.selectedRow.forEach(item => {
|
||||||
|
const st: string = this.scanStatus(item);
|
||||||
|
if (!this.isRunningState(st)) {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isRunningState(state: string): boolean {
|
||||||
|
return state === VULNERABILITY_SCAN_STATUS.RUNNING ||
|
||||||
|
state === VULNERABILITY_SCAN_STATUS.PENDING ||
|
||||||
|
state === VULNERABILITY_SCAN_STATUS.SCHEDULED;
|
||||||
|
}
|
||||||
|
stopNow() {
|
||||||
|
if (this.selectedRow && this.selectedRow.length) {
|
||||||
|
this.scanStoppedArtifactLength = 0;
|
||||||
|
this.onStopScanArtifactsLength = this.selectedRow.length;
|
||||||
|
this.onSendingStopScanCommand = true;
|
||||||
|
this.selectedRow.forEach((data: any) => {
|
||||||
|
let digest = data.digest;
|
||||||
|
this.eventService.publish(HarborEvent.STOP_SCAN_ARTIFACT, this.repoName + "/" + digest);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import { delay } from 'rxjs/operators';
|
|||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { SystemInfo, SystemInfoDefaultService, SystemInfoService, } from "../../../../../../shared/services";
|
import { SystemInfo, SystemInfoDefaultService, SystemInfoService, } from "../../../../../../shared/services";
|
||||||
import { ArtifactDefaultService, ArtifactService } from "../../artifact.service";
|
import { ArtifactDefaultService, ArtifactService } from "../../artifact.service";
|
||||||
import { ChannelService } from "../../../../../../shared/services/channel.service";
|
|
||||||
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../../shared/units/error-handler";
|
||||||
import { RepositoryService as NewRepositoryService } from "../../../../../../../../ng-swagger-gen/services/repository.service";
|
import { RepositoryService as NewRepositoryService } from "../../../../../../../../ng-swagger-gen/services/repository.service";
|
||||||
import { SharedTestingModule } from "../../../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../../../shared/shared.module";
|
||||||
@ -35,9 +34,6 @@ describe('ArtifactListComponent (inline template)', () => {
|
|||||||
},
|
},
|
||||||
snapshot: { data: null }
|
snapshot: { data: null }
|
||||||
};
|
};
|
||||||
let mockChannelService = {
|
|
||||||
scanCommand$: of(1)
|
|
||||||
};
|
|
||||||
let mockSystemInfo: SystemInfo = {
|
let mockSystemInfo: SystemInfo = {
|
||||||
'with_notary': true,
|
'with_notary': true,
|
||||||
'with_admiral': false,
|
'with_admiral': false,
|
||||||
@ -72,7 +68,6 @@ describe('ArtifactListComponent (inline template)', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
{ provide: ErrorHandler, useValue: fakedErrorHandler },
|
||||||
{ provide: ChannelService, useValue: mockChannelService },
|
|
||||||
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
{ provide: SystemInfoService, useClass: SystemInfoDefaultService },
|
||||||
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
{ provide: ArtifactService, useClass: ArtifactDefaultService },
|
||||||
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
{ provide: ActivatedRoute, useValue: mockActivatedRoute },
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
<div *ngIf="completed" class="bar-state bar-state-chart">
|
<div *ngIf="completed" class="bar-state bar-state-chart">
|
||||||
<hbr-result-tip-histogram [scanner]="getScanner()" [vulnerabilitySummary]="summary"></hbr-result-tip-histogram>
|
<hbr-result-tip-histogram [scanner]="getScanner()" [vulnerabilitySummary]="summary"></hbr-result-tip-histogram>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="stopped" class="bar-state">
|
||||||
|
<span class="label stopped">{{'VULNERABILITY.STATE.STOPPED' | translate}}</span>
|
||||||
|
</div>
|
||||||
<div *ngIf="otherStatus" class="bar-state">
|
<div *ngIf="otherStatus" class="bar-state">
|
||||||
<span class="label not-scan">{{'VULNERABILITY.STATE.OTHER_STATUS' | translate}}</span>
|
<span class="label not-scan">{{'VULNERABILITY.STATE.OTHER_STATUS' | translate}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,6 @@ import { HistogramChartComponent } from "./histogram-chart/histogram-chart.compo
|
|||||||
import { JobLogDefaultService, JobLogService, ScanningResultDefaultService, ScanningResultService, } from "../../../../../shared/services";
|
import { JobLogDefaultService, JobLogService, ScanningResultDefaultService, ScanningResultService, } from "../../../../../shared/services";
|
||||||
import { VULNERABILITY_SCAN_STATUS } from "../../../../../shared/units/utils";
|
import { VULNERABILITY_SCAN_STATUS } from "../../../../../shared/units/utils";
|
||||||
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
||||||
import { ChannelService } from "../../../../../shared/services/channel.service";
|
|
||||||
import { ArtifactDefaultService, ArtifactService } from "../artifact.service";
|
import { ArtifactDefaultService, ArtifactService } from "../artifact.service";
|
||||||
import { NativeReportSummary } from "../../../../../../../ng-swagger-gen/models/native-report-summary";
|
import { NativeReportSummary } from "../../../../../../../ng-swagger-gen/models/native-report-summary";
|
||||||
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
||||||
@ -40,7 +39,6 @@ describe('ResultBarChartComponent (inline template)', () => {
|
|||||||
HistogramChartComponent],
|
HistogramChartComponent],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
ChannelService,
|
|
||||||
ArtifactDefaultService,
|
ArtifactDefaultService,
|
||||||
{ provide: ArtifactService, useValue: ArtifactDefaultService },
|
{ provide: ArtifactService, useValue: ArtifactDefaultService },
|
||||||
{ provide: ScanningResultService, useValue: ScanningResultDefaultService },
|
{ provide: ScanningResultService, useValue: ScanningResultDefaultService },
|
||||||
@ -61,15 +59,14 @@ describe('ResultBarChartComponent (inline template)', () => {
|
|||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
it('should show "scan stopped" if status is STOPPED', () => {
|
||||||
it('should show "not scanned" if status is STOPPED', () => {
|
|
||||||
component.summary.scan_status = VULNERABILITY_SCAN_STATUS.STOPPED;
|
component.summary.scan_status = VULNERABILITY_SCAN_STATUS.STOPPED;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
let el: HTMLElement = fixture.nativeElement.querySelector('span');
|
let el: HTMLElement = fixture.nativeElement.querySelector('span');
|
||||||
expect(el).toBeTruthy();
|
expect(el).toBeTruthy();
|
||||||
expect(el.textContent).toEqual('VULNERABILITY.STATE.OTHER_STATUS');
|
expect(el.textContent).toEqual('VULNERABILITY.STATE.STOPPED');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, } from '@angular/core';
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, } from '@angular/core';
|
||||||
import { Subscription, timer } from "rxjs";
|
import { Subscription, timer } from "rxjs";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ScannerVo, ScanningResultService } from "../../../../../shared/services";
|
import { ScannerVo } from "../../../../../shared/services";
|
||||||
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
||||||
import { ChannelService } from "../../../../../shared/services/channel.service";
|
|
||||||
import { clone, CURRENT_BASE_HREF, dbEncodeURIComponent, VULNERABILITY_SCAN_STATUS } from "../../../../../shared/units/utils";
|
import { clone, CURRENT_BASE_HREF, dbEncodeURIComponent, VULNERABILITY_SCAN_STATUS } from "../../../../../shared/units/utils";
|
||||||
import { ArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
import { ArtifactService } from "../../../../../../../ng-swagger-gen/services/artifact.service";
|
||||||
import { Artifact } from "../../../../../../../ng-swagger-gen/models/artifact";
|
import { Artifact } from "../../../../../../../ng-swagger-gen/models/artifact";
|
||||||
import { NativeReportSummary } from "../../../../../../../ng-swagger-gen/models/native-report-summary";
|
import { NativeReportSummary } from "../../../../../../../ng-swagger-gen/models/native-report-summary";
|
||||||
|
import { EventService, HarborEvent } from "../../../../../services/event-service/event.service";
|
||||||
|
import { ScanService } from "../../../../../../../ng-swagger-gen/services/scan.service";
|
||||||
|
|
||||||
|
|
||||||
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
||||||
@ -25,20 +26,25 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
@Input() artifactDigest: string = "";
|
@Input() artifactDigest: string = "";
|
||||||
@Input() summary: NativeReportSummary;
|
@Input() summary: NativeReportSummary;
|
||||||
onSubmitting: boolean = false;
|
onSubmitting: boolean = false;
|
||||||
|
onStopping: boolean = false;
|
||||||
retryCounter: number = 0;
|
retryCounter: number = 0;
|
||||||
stateCheckTimer: Subscription;
|
stateCheckTimer: Subscription;
|
||||||
scanSubscription: Subscription;
|
scanSubscription: Subscription;
|
||||||
|
stopSubscription: Subscription;
|
||||||
timerHandler: any;
|
timerHandler: any;
|
||||||
@Output()
|
@Output()
|
||||||
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||||
|
// if sending stop scan request is finished, emit to farther component
|
||||||
|
@Output()
|
||||||
|
submitStopFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
|
||||||
@Output()
|
@Output()
|
||||||
scanFinished: EventEmitter<Artifact> = new EventEmitter<Artifact>();
|
scanFinished: EventEmitter<Artifact> = new EventEmitter<Artifact>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private artifactService: ArtifactService,
|
private artifactService: ArtifactService,
|
||||||
private scanningService: ScanningResultService,
|
private scanService: ScanService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private channel: ChannelService,
|
private eventService: EventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -50,13 +56,23 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
this.getSummary();
|
this.getSummary();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.scanSubscription = this.channel.scanCommand$.subscribe((artifactDigest: string) => {
|
if (!this.scanSubscription) {
|
||||||
|
this.scanSubscription = this.eventService.subscribe( HarborEvent.START_SCAN_ARTIFACT, (artifactDigest: string) => {
|
||||||
let myFullTag: string = this.repoName + "/" + this.artifactDigest;
|
let myFullTag: string = this.repoName + "/" + this.artifactDigest;
|
||||||
if (myFullTag === artifactDigest) {
|
if (myFullTag === artifactDigest) {
|
||||||
this.scanNow();
|
this.scanNow();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!this.stopSubscription) {
|
||||||
|
this.stopSubscription = this.eventService.subscribe( HarborEvent.STOP_SCAN_ARTIFACT, (artifactDigest: string) => {
|
||||||
|
let myFullTag: string = this.repoName + "/" + this.artifactDigest;
|
||||||
|
if (myFullTag === artifactDigest) {
|
||||||
|
this.stopScan();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.stateCheckTimer) {
|
if (this.stateCheckTimer) {
|
||||||
@ -65,6 +81,11 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
if (this.scanSubscription) {
|
if (this.scanSubscription) {
|
||||||
this.scanSubscription.unsubscribe();
|
this.scanSubscription.unsubscribe();
|
||||||
|
this.scanSubscription = null;
|
||||||
|
}
|
||||||
|
if (!this.stopSubscription) {
|
||||||
|
this.stopSubscription.unsubscribe();
|
||||||
|
this.stopSubscription = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +112,11 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
public get scanning(): boolean {
|
public get scanning(): boolean {
|
||||||
return this.status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
return this.status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
||||||
}
|
}
|
||||||
|
public get stopped(): boolean {
|
||||||
|
return this.status === VULNERABILITY_SCAN_STATUS.STOPPED;
|
||||||
|
}
|
||||||
public get otherStatus(): boolean {
|
public get otherStatus(): boolean {
|
||||||
return !(this.completed || this.error || this.queued || this.scanning);
|
return !(this.completed || this.error || this.queued || this.scanning || this.stopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
scanNow(): void {
|
scanNow(): void {
|
||||||
@ -109,8 +133,11 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.onSubmitting = true;
|
this.onSubmitting = true;
|
||||||
|
|
||||||
this.scanningService.startVulnerabilityScanning(this.projectName, dbEncodeURIComponent(this.repoName), this.artifactDigest)
|
this.scanService.scanArtifact({
|
||||||
.pipe(finalize(() => this.submitFinish.emit(false)))
|
projectName: this.projectName,
|
||||||
|
reference: this.artifactDigest,
|
||||||
|
repositoryName: dbEncodeURIComponent(this.repoName)
|
||||||
|
}).pipe(finalize(() => this.submitFinish.emit(false)))
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.onSubmitting = false;
|
this.onSubmitting = false;
|
||||||
// Forcely change status to queued after successful submitting
|
// Forcely change status to queued after successful submitting
|
||||||
@ -157,7 +184,7 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.scanFinished.emit(artifact);
|
this.scanFinished.emit(artifact);
|
||||||
}
|
}
|
||||||
this.channel.ArtifactDetail$.next(artifact);
|
this.eventService.publish(HarborEvent.UPDATE_VULNERABILITY_INFO, artifact);
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
this.retryCounter++;
|
this.retryCounter++;
|
||||||
@ -186,4 +213,38 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
return this.inputScanner;
|
return this.inputScanner;
|
||||||
}
|
}
|
||||||
|
stopScan() {
|
||||||
|
if (this.onStopping) {
|
||||||
|
// Avoid duplicated stopping command
|
||||||
|
console.log("duplicated stopping command");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.repoName || !this.artifactDigest) {
|
||||||
|
console.log("bad repository or artifact");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onStopping = true;
|
||||||
|
|
||||||
|
this.scanService.stopScanArtifact({
|
||||||
|
projectName: this.projectName,
|
||||||
|
reference: this.artifactDigest,
|
||||||
|
repositoryName: dbEncodeURIComponent(this.repoName)
|
||||||
|
}).pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.submitStopFinish.emit(false);
|
||||||
|
this.onStopping = false;
|
||||||
|
}))
|
||||||
|
.subscribe(() => {
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.errorHandler.info('VULNERABILITY.TRIGGER_STOP_SUCCESS');
|
||||||
|
}, error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,3 +169,6 @@ hbr-vulnerability-bar {
|
|||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.stopped {
|
||||||
|
border-color: #cccc15;
|
||||||
|
}
|
||||||
|
@ -76,5 +76,8 @@ export class EventService {
|
|||||||
export enum HarborEvent {
|
export enum HarborEvent {
|
||||||
SCROLL = 'scroll',
|
SCROLL = 'scroll',
|
||||||
SCROLL_TO_POSITION = 'scrollToPosition',
|
SCROLL_TO_POSITION = 'scrollToPosition',
|
||||||
REFRESH_PROJECT_INFO = 'refreshProjectInfo'
|
REFRESH_PROJECT_INFO = 'refreshProjectInfo',
|
||||||
|
START_SCAN_ARTIFACT = 'startScanArtifact',
|
||||||
|
STOP_SCAN_ARTIFACT = 'stopScanArtifact',
|
||||||
|
UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo'
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
|
import { waitForAsync, ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { ImageNameInputComponent } from "./image-name-input.component";
|
import { ImageNameInputComponent } from "./image-name-input.component";
|
||||||
import { ErrorHandler } from "../../units/error-handler/error-handler";
|
|
||||||
import { ProjectDefaultService, ProjectService } from "../../services";
|
import { ProjectDefaultService, ProjectService } from "../../services";
|
||||||
import { Project } from "../../../base/project/project-config/project-policy-config/project";
|
import { Project } from "../../../base/project/project-config/project-policy-config/project";
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { HttpResponse } from "@angular/common/http";
|
import { HttpResponse } from "@angular/common/http";
|
||||||
import { ChannelService } from "../../services/channel.service";
|
|
||||||
import { CURRENT_BASE_HREF } from "../../units/utils";
|
|
||||||
import { SharedTestingModule } from "../../shared.module";
|
import { SharedTestingModule } from "../../shared.module";
|
||||||
|
|
||||||
describe("ImageNameInputComponent (inline template)", () => {
|
describe("ImageNameInputComponent (inline template)", () => {
|
||||||
@ -35,8 +32,6 @@ describe("ImageNameInputComponent (inline template)", () => {
|
|||||||
ImageNameInputComponent
|
ImageNameInputComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
|
||||||
ChannelService,
|
|
||||||
{ provide: ProjectService, useClass: ProjectDefaultService }
|
{ provide: ProjectService, useClass: ProjectDefaultService }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Subject } from "rxjs";
|
|
||||||
import { ArtifactFront as Artifact } from "../../base/project/repository/artifact/artifact";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class ChannelService {
|
|
||||||
|
|
||||||
// Declare for publishing scan event
|
|
||||||
scanCommandSource = new Subject<string>();
|
|
||||||
scanCommand$ = this.scanCommandSource.asObservable();
|
|
||||||
|
|
||||||
publishScanEvent(tagId: string): void {
|
|
||||||
this.scanCommandSource.next(tagId);
|
|
||||||
}
|
|
||||||
ArtifactDetail$ = new Subject<Artifact>();
|
|
||||||
}
|
|
@ -965,6 +965,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Spezifiziere den Namen einer OIDC Administratorengruppe. Alle Mitglieder dieser Gruppe haben in Harbor administrative Berechtigungen. Falls dies nicht gewünscht ist, kann das Feld leer gelassen werden."
|
"OIDC_ADMIN_GROUP_INFO": "Spezifiziere den Namen einer OIDC Administratorengruppe. Alle Mitglieder dieser Gruppe haben in Harbor administrative Berechtigungen. Falls dies nicht gewünscht ist, kann das Feld leer gelassen werden."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Scan erfolgreich gestartet!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Scan erfolgreich gestartet!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Fehler beim Start des Scans: {{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Fehler beim Start des Scans: {{error}}",
|
||||||
"TITLE": "Schwachstellen-Scanning",
|
"TITLE": "Schwachstellen-Scanning",
|
||||||
@ -1035,7 +1036,8 @@
|
|||||||
"OTHER_STATUS": "Nicht gescannt",
|
"OTHER_STATUS": "Nicht gescannt",
|
||||||
"QUEUED": "In Warteschlange",
|
"QUEUED": "In Warteschlange",
|
||||||
"ERROR": "Log anzeigen",
|
"ERROR": "Log anzeigen",
|
||||||
"SCANNING": "Scanning"
|
"SCANNING": "Scanning",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "Es wurden keine Scan-Ergebnisse gefunden!",
|
"PLACEHOLDER": "Es wurden keine Scan-Ergebnisse gefunden!",
|
||||||
@ -1078,7 +1080,9 @@
|
|||||||
"SCAN_NOW": "Scan",
|
"SCAN_NOW": "Scan",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Push Befehl",
|
"TITLE": "Push Befehl",
|
||||||
@ -1697,6 +1701,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "Der zusammengesetzte Robot-Account-Name besteht aus dem Prefix, dem Projektnamen, einem plus und dem Inhalt des Eingabefeldes.",
|
"FINAL_PROJECT_NAME_TIP": "Der zusammengesetzte Robot-Account-Name besteht aus dem Prefix, dem Projektnamen, einem plus und dem Inhalt des Eingabefeldes.",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "Der zusammengesetzte systemweite Robot-Account-Name besteht aus dem Prefix und dem Inhalt des Eingabefeldes.",
|
"FINAL_SYSTEM_NAME_TIP": "Der zusammengesetzte systemweite Robot-Account-Name besteht aus dem Prefix und dem Inhalt des Eingabefeldes.",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -965,6 +965,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Failed to trigger scan all with error: {{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Failed to trigger scan all with error: {{error}}",
|
||||||
"TITLE": "Vulnerability Scanning",
|
"TITLE": "Vulnerability Scanning",
|
||||||
@ -1035,7 +1036,8 @@
|
|||||||
"OTHER_STATUS": "Not Scanned",
|
"OTHER_STATUS": "Not Scanned",
|
||||||
"QUEUED": "Queued",
|
"QUEUED": "Queued",
|
||||||
"ERROR": "View Log",
|
"ERROR": "View Log",
|
||||||
"SCANNING": "Scanning"
|
"SCANNING": "Scanning",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "We couldn't find any scanning results!",
|
"PLACEHOLDER": "We couldn't find any scanning results!",
|
||||||
@ -1078,7 +1080,9 @@
|
|||||||
"SCAN_NOW": "Scan",
|
"SCAN_NOW": "Scan",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Push Command",
|
"TITLE": "Push Command",
|
||||||
@ -1697,6 +1701,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -966,6 +966,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Trigger scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Failed to trigger scan all with error: {{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Failed to trigger scan all with error: {{error}}",
|
||||||
"TITLE": "Vulnerability Scanning",
|
"TITLE": "Vulnerability Scanning",
|
||||||
@ -1036,7 +1037,8 @@
|
|||||||
"OTHER_STATUS": "Not Scanned",
|
"OTHER_STATUS": "Not Scanned",
|
||||||
"QUEUED": "Queued",
|
"QUEUED": "Queued",
|
||||||
"ERROR": "View Log",
|
"ERROR": "View Log",
|
||||||
"SCANNING": "Scanning"
|
"SCANNING": "Scanning",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "We couldn't find any scanning results!",
|
"PLACEHOLDER": "We couldn't find any scanning results!",
|
||||||
@ -1079,7 +1081,9 @@
|
|||||||
"SCAN_NOW": "Scan",
|
"SCAN_NOW": "Scan",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Push Command",
|
"TITLE": "Push Command",
|
||||||
@ -1696,6 +1700,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -938,6 +938,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Déclenchement d'analyse globale avec succès !",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Déclenchement d'analyse globale avec succès !",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Echec du déclenchement d'analyse globale avec des erreurs : {{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Echec du déclenchement d'analyse globale avec des erreurs : {{error}}",
|
||||||
"TITLE": "Analyse de vulnérabilité",
|
"TITLE": "Analyse de vulnérabilité",
|
||||||
@ -1008,7 +1009,8 @@
|
|||||||
"OTHER_STATUS": "Non Analysé",
|
"OTHER_STATUS": "Non Analysé",
|
||||||
"QUEUED": "En fil d'attente",
|
"QUEUED": "En fil d'attente",
|
||||||
"ERROR": "Voir le Log",
|
"ERROR": "Voir le Log",
|
||||||
"SCANNING": "En cours d'analyse"
|
"SCANNING": "En cours d'analyse",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "Nous n'avons pas trouvé de résultats d'analyse !",
|
"PLACEHOLDER": "Nous n'avons pas trouvé de résultats d'analyse !",
|
||||||
@ -1051,7 +1053,9 @@
|
|||||||
"SCAN_NOW": "Analyser",
|
"SCAN_NOW": "Analyser",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Push Command",
|
"TITLE": "Push Command",
|
||||||
@ -1665,6 +1669,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -961,6 +961,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Informe o nome do grupo OIDC que será considerado administrativo. Todos os usuários deste grupo obterão privilégios de adiministração no Harbor. Deixe vazio para ser ignorado."
|
"OIDC_ADMIN_GROUP_INFO": "Informe o nome do grupo OIDC que será considerado administrativo. Todos os usuários deste grupo obterão privilégios de adiministração no Harbor. Deixe vazio para ser ignorado."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Disparo de análise geral efetuado com sucesso!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Disparo de análise geral efetuado com sucesso!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Falha ao disparar análise geral com erro: {{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Falha ao disparar análise geral com erro: {{error}}",
|
||||||
"TITLE": "Análise de vulnerabilidades",
|
"TITLE": "Análise de vulnerabilidades",
|
||||||
@ -1031,7 +1032,8 @@
|
|||||||
"OTHER_STATUS": "Não analisado",
|
"OTHER_STATUS": "Não analisado",
|
||||||
"QUEUED": "Solicitado",
|
"QUEUED": "Solicitado",
|
||||||
"ERROR": "Ver erros...",
|
"ERROR": "Ver erros...",
|
||||||
"SCANNING": "Analisando"
|
"SCANNING": "Analisando",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "Não foi possível encontrar nenhum resultado de análise!",
|
"PLACEHOLDER": "Não foi possível encontrar nenhum resultado de análise!",
|
||||||
@ -1074,7 +1076,9 @@
|
|||||||
"SCAN_NOW": "Analisar",
|
"SCAN_NOW": "Analisar",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Relatado por {{scanner}}",
|
"REPORTED_BY": "Relatado por {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Comando Push",
|
"TITLE": "Comando Push",
|
||||||
@ -1693,7 +1697,8 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "Para evitar conflitos entre projetos, o nome completo será composto pelo nome do projeto concatenado a um marcador e o valor aqui informado.",
|
"FINAL_PROJECT_NAME_TIP": "Para evitar conflitos entre projetos, o nome completo será composto pelo nome do projeto concatenado a um marcador e o valor aqui informado.",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "Este valor será concatenado ao prefixo do projeto.",
|
"FINAL_SYSTEM_NAME_TIP": "Este valor será concatenado ao prefixo do projeto.",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Permissões de envio (push) presume também a permissão e recebimento (pull)."
|
"PUSH_PERMISSION_TOOLTIP": "Permissões de envio (push) presume também a permissão e recebimento (pull).",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -965,6 +965,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "Tümünü başarılı bir şekilde tara!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "Tümünü başarılı bir şekilde tara!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "Tüm taramayı hatayla tetikleyemedi:{{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "Tüm taramayı hatayla tetikleyemedi:{{error}}",
|
||||||
"TITLE": "Güvenlik açığı taraması",
|
"TITLE": "Güvenlik açığı taraması",
|
||||||
@ -1035,7 +1036,8 @@
|
|||||||
"OTHER_STATUS": "Taranmadı",
|
"OTHER_STATUS": "Taranmadı",
|
||||||
"QUEUED": "Sıraya alındı",
|
"QUEUED": "Sıraya alındı",
|
||||||
"ERROR": "Günlüğü Görüntüle",
|
"ERROR": "Günlüğü Görüntüle",
|
||||||
"SCANNING": "Taranıyor"
|
"SCANNING": "Taranıyor",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "Herhangi bir tarama sonucu bulamadık!",
|
"PLACEHOLDER": "Herhangi bir tarama sonucu bulamadık!",
|
||||||
@ -1078,7 +1080,9 @@
|
|||||||
"SCAN_NOW": "Tara",
|
"SCAN_NOW": "Tara",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "Push Command",
|
"TITLE": "Push Command",
|
||||||
@ -1697,6 +1701,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -966,6 +966,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "OIDC管理员组名称。所有该组内用户都会有管理员权限,此属性可以为空。"
|
"OIDC_ADMIN_GROUP_INFO": "OIDC管理员组名称。所有该组内用户都会有管理员权限,此属性可以为空。"
|
||||||
},
|
},
|
||||||
"SCANNING": {
|
"SCANNING": {
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "停止扫描所有镜像任务成功!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "启动扫描所有镜像任务成功!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "启动扫描所有镜像任务成功!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "启动扫描所有镜像任务失败:{{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "启动扫描所有镜像任务失败:{{error}}",
|
||||||
"TITLE": "缺陷扫描",
|
"TITLE": "缺陷扫描",
|
||||||
@ -1036,7 +1037,8 @@
|
|||||||
"OTHER_STATUS": "未扫描",
|
"OTHER_STATUS": "未扫描",
|
||||||
"QUEUED": "已入队列",
|
"QUEUED": "已入队列",
|
||||||
"ERROR": "查看日志",
|
"ERROR": "查看日志",
|
||||||
"SCANNING": "扫描中"
|
"SCANNING": "扫描中",
|
||||||
|
"STOPPED": "扫描中止"
|
||||||
},
|
},
|
||||||
"GRID": {
|
"GRID": {
|
||||||
"PLACEHOLDER": "没有扫描结果!",
|
"PLACEHOLDER": "没有扫描结果!",
|
||||||
@ -1079,7 +1081,9 @@
|
|||||||
"SCAN_NOW": "扫描",
|
"SCAN_NOW": "扫描",
|
||||||
"SCAN_BY": "使用 {{scanner}} 进行扫描",
|
"SCAN_BY": "使用 {{scanner}} 进行扫描",
|
||||||
"REPORTED_BY": "结果由 {{scanner}} 提供",
|
"REPORTED_BY": "结果由 {{scanner}} 提供",
|
||||||
"NO_SCANNER": "无扫描器"
|
"NO_SCANNER": "无扫描器",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "停止扫描成功",
|
||||||
|
"STOP_NOW": "停止扫描"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE": {
|
"PUSH_IMAGE": {
|
||||||
"TITLE": "推送命令",
|
"TITLE": "推送命令",
|
||||||
@ -1695,6 +1699,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "项目级机器人的最终名称由前缀,项目名称,一个加号以及当前输入值组成",
|
"FINAL_PROJECT_NAME_TIP": "项目级机器人的最终名称由前缀,项目名称,一个加号以及当前输入值组成",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "系统级机器人的最终名称由前缀和当前输入值组成",
|
"FINAL_SYSTEM_NAME_TIP": "系统级机器人的最终名称由前缀和当前输入值组成",
|
||||||
"PUSH_AND_PULL": "推送",
|
"PUSH_AND_PULL": "推送",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "推送权限无法单独工作,请在选择推送权限的时,确保已经勾选了拉取权限"
|
"PUSH_PERMISSION_TOOLTIP": "推送权限无法单独工作,请在选择推送权限的时,确保已经勾选了拉取权限",
|
||||||
|
"STOP": "停止"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -961,6 +961,7 @@
|
|||||||
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
"OIDC_ADMIN_GROUP_INFO": "Specify an OIDC admin group name. All OIDC users in this group will have harbor admin privilege. Keep it blank if you do not want to."
|
||||||
},
|
},
|
||||||
"SCANNING":{
|
"SCANNING":{
|
||||||
|
"STOP_SCAN_ALL_SUCCESS": "Trigger stopping scan all successfully!",
|
||||||
"TRIGGER_SCAN_ALL_SUCCESS": "啟動掃描所有鏡像任務成功!",
|
"TRIGGER_SCAN_ALL_SUCCESS": "啟動掃描所有鏡像任務成功!",
|
||||||
"TRIGGER_SCAN_ALL_FAIL": "啟動掃描所有鏡像任務失敗:{{error}}",
|
"TRIGGER_SCAN_ALL_FAIL": "啟動掃描所有鏡像任務失敗:{{error}}",
|
||||||
"TITLE": "缺陷掃描",
|
"TITLE": "缺陷掃描",
|
||||||
@ -1031,7 +1032,8 @@
|
|||||||
"OTHER_STATUS": "未掃描",
|
"OTHER_STATUS": "未掃描",
|
||||||
"QUEUED": "已入隊列",
|
"QUEUED": "已入隊列",
|
||||||
"ERROR": "查看日誌",
|
"ERROR": "查看日誌",
|
||||||
"SCANNING": "掃描中"
|
"SCANNING": "掃描中",
|
||||||
|
"STOPPED": "Scan stopped"
|
||||||
},
|
},
|
||||||
"GRID":{
|
"GRID":{
|
||||||
"PLACEHOLDER": "沒有掃描結果!",
|
"PLACEHOLDER": "沒有掃描結果!",
|
||||||
@ -1074,7 +1076,9 @@
|
|||||||
"SCAN_NOW":"掃描",
|
"SCAN_NOW":"掃描",
|
||||||
"SCAN_BY": "SCAN BY {{scanner}}",
|
"SCAN_BY": "SCAN BY {{scanner}}",
|
||||||
"REPORTED_BY": "Reported by {{scanner}}",
|
"REPORTED_BY": "Reported by {{scanner}}",
|
||||||
"NO_SCANNER": "NO SCANNER"
|
"NO_SCANNER": "NO SCANNER",
|
||||||
|
"TRIGGER_STOP_SUCCESS": "Trigger stopping scan successfully",
|
||||||
|
"STOP_NOW": "Stop Scan"
|
||||||
},
|
},
|
||||||
"PUSH_IMAGE":{
|
"PUSH_IMAGE":{
|
||||||
"TITLE": "推送鏡像的Docker命令",
|
"TITLE": "推送鏡像的Docker命令",
|
||||||
@ -1682,6 +1686,7 @@
|
|||||||
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
"FINAL_PROJECT_NAME_TIP": "The final project robot name consists of the prefix,the project name,a plus mark and the current input value",
|
||||||
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
"FINAL_SYSTEM_NAME_TIP": "The final system robot name consists of the prefix and the current input value",
|
||||||
"PUSH_AND_PULL": "Push",
|
"PUSH_AND_PULL": "Push",
|
||||||
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission"
|
"PUSH_PERMISSION_TOOLTIP": "Push permission can not work alone, it must work with pull permission",
|
||||||
|
"STOP": "Stop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user