Refactoring vulnerability ui support cron

Signed-off-by: FangyuanCheng <fangyuanc@vmware.com>
This commit is contained in:
FangyuanCheng 2019-03-13 16:04:17 +08:00
parent e538a4c448
commit dada47c07e
14 changed files with 196 additions and 247 deletions

View File

@ -54,8 +54,8 @@ export class GcComponent implements OnInit {
}
public initSchedule(schedule: any) {
if (schedule && schedule.length > 0) {
this.schedule = schedule[0];
if (schedule && schedule.schedule !== null) {
this.schedule = schedule;
this.originCron = this.schedule.schedule;
} else {
this.originCron = {

View File

@ -10,7 +10,7 @@
<clr-tab>
<button id="config-vulnerability" clrTabLink>{{'CONFIG.VULNERABILITY' | translate}}</button>
<clr-tab-content id="vulnerability" *clrIfActive>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true"></vulnerability-config>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [showSubTitle]="true"></vulnerability-config>
</clr-tab-content>
</clr-tab>
<clr-tab>

View File

@ -0,0 +1,42 @@
import { Injectable, Inject } from '@angular/core';
import { Http } from '@angular/http';
import { throwError as observableThrowError, Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { SERVICE_CONFIG, IServiceConfig } from "../../service.config";
export abstract class ScanApiRepository {
abstract postSchedule(param): Observable<any>;
abstract putSchedule(param): Observable<any>;
abstract getSchedule(): Observable<any>;
}
@Injectable()
export class ScanApiDefaultRepository extends ScanApiRepository {
constructor(
private http: Http,
@Inject(SERVICE_CONFIG) private config: IServiceConfig
) {
super();
}
public postSchedule(param): Observable<any> {
return this.http.post(`${this.config.ScanAllEndpoint}/schedule`, param)
.pipe(catchError(error => observableThrowError(error)));
}
public putSchedule(param): Observable<any> {
return this.http.put(`${this.config.ScanAllEndpoint}/schedule`, param)
.pipe(catchError(error => observableThrowError(error)));
}
public getSchedule(): Observable<any> {
return this.http.get(`${this.config.ScanAllEndpoint}/schedule`)
.pipe(catchError(error => observableThrowError(error)))
.pipe(map(response => response.json()));
}
}

View File

@ -0,0 +1,50 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs';
import { ScanApiRepository } from './scanAll.api.repository';
import { ErrorHandler } from '../../error-handler/index';
@Injectable()
export class ScanAllRepoService {
constructor(private http: Http,
private scanApiRepository: ScanApiRepository,
private errorHandler: ErrorHandler) {
}
public manualScan(): Observable <any> {
let param = {
"schedule": {
"type": "Manual"
}
};
return this.scanApiRepository.postSchedule(param);
}
public getSchedule(): Observable <any> {
return this.scanApiRepository.getSchedule();
}
public postSchedule(type, cron): Observable <any> {
let param = {
"schedule": {
"type": type,
"cron": cron,
}
};
return this.scanApiRepository.postSchedule(param);
}
public putSchedule(type, cron): Observable <any> {
let param = {
"schedule": {
"type": type,
"cron": cron,
}
};
return this.scanApiRepository.putSchedule(param);
}
}

View File

@ -23,30 +23,7 @@
</clr-dropdown>
<span *ngIf="isClairDBFullyReady && !showScanningNamespaces">{{ updatedTimestamp | date:'MM/dd/y HH:mm:ss' }} AM</span>
</div>
<div class="form-group vertical-center normal-wrapper" *ngIf="!isEditMode">
<span>{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</span>
<span>{{ (scanningType ? 'SCHEDULE.'+ scanningType.toUpperCase(): "") | translate }}</span>
<span [hidden]="scanningType===SCHEDULE_TYPE.NONE">{{'SCHEDULE.AT' | translate}}</span>
<span [hidden]="scanningType===SCHEDULE_TYPE.NONE">{{ dailyTime | translate }} AM</span>
<button class="btn btn-outline btn-sm" (click)="editSchedule()" id="editSchedule">{{'BUTTON.EDIT' | translate}}</button>
</div>
<div class="form-group vertical-center" *ngIf="isEditMode">
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
<div class="select">
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="scanningType">
<option value="none">{{ 'SCHEDULE.NONE' | translate }}</option>
<option value="daily">{{ 'SCHEDULE.DAILY' | translate }}</option>
</select>
</div>
<span [hidden]="!showTimePicker">{{'SCHEDULE.AT' | translate}}</span>
<input type="time" name="dailyTimePicker" required [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.TOOLTIP.SCANNING_POLICY' | translate}}</span>
</a>
<button id="config_vulnerbility_save" class="btn btn-primary btn-sm" (click)="save()">{{'BUTTON.SAVE' | translate}}</button>
<button class="btn btn-primary btn-sm" (click)="cancel()" >{{'BUTTON.CANCEL' | translate}}</button>
</div>
<cron-selection [labelCurrent]="getLabelCurrent" [labelEdit]='getLabelCurrent' [originCron]='originCron' (inputvalue)="scanAll($event)"></cron-selection>
<div class="btn-scan-right btn-scan">
<button class="btn btn-primary btn-sm btn-scan" (click)="scanNow()" [disabled]="!scanAvailable">{{ 'CONFIG.SCANNING.SCAN_NOW' | translate }}</button><br>
</div>

View File

@ -13,12 +13,12 @@ import { ErrorHandler } from '../../error-handler/index';
import { isEmptyObject, clone} from '../../utils';
import { TranslateService } from '@ngx-translate/core';
import { ClairDetail } from '../../service/interface';
import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../service/interface';
import { CronScheduleComponent } from "../../cron-schedule/cron-schedule.component";
const ONE_HOUR_SECONDS: number = 3600;
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
const SCHEDULE_TYPE = {
NONE: "none",
DAILY: "daily"
};
const SCHEDULE_TYPE_NONE = "None";
@Component({
selector: 'vulnerability-config',
templateUrl: './vulnerability-config.component.html',
@ -26,39 +26,25 @@ const SCHEDULE_TYPE = {
})
export class VulnerabilityConfigComponent implements OnInit {
_localTime: Date = new Date();
isEditMode: boolean = false;
SCHEDULE_TYPE = SCHEDULE_TYPE;
configCopy: Configuration;
originCron: OriginCron;
schedule: any;
onSubmitting: boolean = false;
config: Configuration;
openState: boolean = false;
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
getLabelCurrent: string;
@ViewChild(CronScheduleComponent)
CronScheduleComponent: CronScheduleComponent;
@Input()
get vulnerabilityConfig(): Configuration {
return this.config;
}
set vulnerabilityConfig(cfg: Configuration) {
this.config = cfg;
if (this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
if (this.config.scan_all_policy.value.type === "daily") {
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
}
}
this.configChange.emit(this.config);
}
@Input() showSubTitle: boolean = false;
@Input() showScanningNamespaces: boolean = false;
systemInfo: SystemInfo;
constructor(
private scanningService: ScanningResultService,
// private scanningService: ScanningResultService,
private scanningService: ScanAllRepoService,
private errorHandler: ErrorHandler,
private translate: TranslateService,
private systemInfoService: SystemInfoService,
@ -69,6 +55,12 @@ export class VulnerabilityConfigComponent implements OnInit {
return !this.onSubmitting;
}
getScanText() {
this.translate.get('CONFIG.SCANNING.SCAN_ALL').subscribe((res: string) => {
this.getLabelCurrent = res;
});
}
get updatedTimestamp(): Date {
if (this.systemInfo &&
this.systemInfo.clair_vulnerability_status &&
@ -90,146 +82,29 @@ export class VulnerabilityConfigComponent implements OnInit {
return [];
}
// UTC time
get dailyTime(): string {
if (!(this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value &&
this.config.scan_all_policy.value.type === "daily")) {
return "00:00";
}
getSchedule() {
this.scanningService.getSchedule().subscribe(schedule => {
this.initSchedule(schedule);
});
}
let timeOffset: number = 0; // seconds
if (this.config.scan_all_policy.value.parameter) {
let daily_time = this.config.scan_all_policy.value.parameter.daily_time;
if (daily_time && typeof daily_time === "number") {
timeOffset = +daily_time;
}
}
// Convert to current time
let timezoneOffset: number = this._localTime.getTimezoneOffset();
// Local time
timeOffset = timeOffset - timezoneOffset * 60;
if (timeOffset < 0) {
timeOffset = timeOffset + ONE_DAY_SECONDS;
}
if (timeOffset >= ONE_DAY_SECONDS) {
timeOffset -= ONE_DAY_SECONDS;
}
// To time string
let hours: number = Math.floor(timeOffset / ONE_HOUR_SECONDS);
let minutes: number = Math.floor((timeOffset - hours * ONE_HOUR_SECONDS) / 60);
let timeStr: string = "" + hours;
if (hours < 10) {
timeStr = "0" + timeStr;
}
if (minutes < 10) {
timeStr += ":0";
public initSchedule(schedule: any) {
if (schedule && schedule.schedule !== null) {
this.schedule = schedule;
this.originCron = this.schedule.schedule;
} else {
timeStr += ":";
this.originCron = {
type: SCHEDULE_TYPE_NONE,
cron: ''
};
}
timeStr += minutes;
return timeStr;
}
set dailyTime(v: string) {
if (!v || v === "") {
return;
}
if (!(this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value &&
this.config.scan_all_policy.value.type === "daily")) {
return;
}
// Double confirm inner parameter existing.
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
let values: string[] = v.split(":");
if (!values || values.length !== 2) {
return;
}
let hours: number = +values[0];
let minutes: number = +values[1];
// Convert to UTC time
let timezoneOffset: number = this._localTime.getTimezoneOffset();
let utcTimes: number = hours * ONE_HOUR_SECONDS + minutes * 60;
utcTimes += timezoneOffset * 60;
if (utcTimes < 0) {
utcTimes += ONE_DAY_SECONDS;
}
if (utcTimes >= ONE_DAY_SECONDS) {
utcTimes -= ONE_DAY_SECONDS;
}
this.config.scan_all_policy.value.parameter.daily_time = utcTimes;
}
// Scanning type
get scanningType(): string {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
return this.config.scan_all_policy.value.type;
} else {
// default
return "none";
}
}
set scanningType(v: string) {
if (this.config &&
this.config.scan_all_policy &&
this.config.scan_all_policy.value) {
let type: string = (v && v.trim() !== "") ? v : "none";
this.config.scan_all_policy.value.type = type;
if (type !== "daily") {
// No parameter
if (this.config.scan_all_policy.value.parameter) {
delete (this.config.scan_all_policy.value.parameter);
}
} else {
// Has parameter
if (!this.config.scan_all_policy.value.parameter) {
this.config.scan_all_policy.value.parameter = {
daily_time: 0
};
}
}
}
}
}
@ViewChild("systemConfigFrom") systemSettingsForm: NgForm;
get editable(): boolean {
return this.vulnerabilityConfig &&
this.vulnerabilityConfig.scan_all_policy &&
this.vulnerabilityConfig.scan_all_policy.editable;
}
get isValid(): boolean {
return this.systemSettingsForm && this.systemSettingsForm.valid;
}
get showTimePicker(): boolean {
return this.vulnerabilityConfig &&
this.vulnerabilityConfig.scan_all_policy &&
this.vulnerabilityConfig.scan_all_policy.value &&
this.vulnerabilityConfig.scan_all_policy.value.type === "daily";
}
get isClairDBFullyReady(): boolean {
return this.systemInfo &&
this.systemInfo.clair_vulnerability_status &&
@ -238,23 +113,10 @@ export class VulnerabilityConfigComponent implements OnInit {
ngOnInit(): void {
this.getSystemInfo();
this.getConfigurations();
this.getScanText();
this.getSchedule();
}
getConfigurations(): void {
this.configService.getConfigurations()
.subscribe((config: Configuration) => {
this.configCopy = clone(config);
this.config = config;
}, error => {
this.errorHandler.error(error);
});
}
editSchedule() {
this.isEditMode = true;
}
convertToLocalTime(utcTime: number): Date {
let dt: Date = new Date();
dt.setTime(utcTime * 1000);
@ -272,7 +134,7 @@ export class VulnerabilityConfigComponent implements OnInit {
}
this.onSubmitting = true;
this.scanningService.startScanningAll()
this.scanningService.manualScan()
.subscribe(() => {
this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_SUCCESS").subscribe((res: string) => {
this.errorHandler.info(res);
@ -306,38 +168,43 @@ export class VulnerabilityConfigComponent implements OnInit {
}));
}
save(): void {
let getchanges = this.config.scan_all_policy.value;
let changes = {"scan_all_policy": getchanges};
reset(cron): void {
this.schedule = {
schedule: {
type: this.CronScheduleComponent.scheduleType,
cron: cron
}
};
}
if (isEmptyObject(changes)) {
return;
}
this.configService.saveConfigurations(changes)
.subscribe(() => {
this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res: string) => {
this.errorHandler.info(res);
scanAll(cron: string): void {
let schedule = this.schedule;
if (schedule && schedule.schedule && schedule.schedule.type !== SCHEDULE_TYPE_NONE) {
this.scanningService.putSchedule(this.CronScheduleComponent.scheduleType, cron)
.subscribe(response => {
this.translate
.get("CONFIG.SAVE_SUCCESS")
.subscribe((res) => {
this.errorHandler.info(res);
});
this.getConfigurations();
this.isEditMode = false;
}, error => {
this.errorHandler.error(error);
this.reset();
});
}
cancel(): void {
this.reset();
this.isEditMode = false;
}
reset(): void {
// Reset to the values of copy
let getchanges = this.config.scan_all_policy.value;
let changes = {"scan_all_policy": getchanges};
for (let prop of Object.keys(changes)) {
this.config[prop] = clone(this.configCopy[prop]);
this.reset(cron);
},
error => {
this.errorHandler.error(error);
}
);
} else {
this.scanningService.postSchedule(this.CronScheduleComponent.scheduleType, cron)
.subscribe(response => {
this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res) => {
this.errorHandler.info(res);
});
this.reset(cron);
},
error => {
this.errorHandler.error(error);
}
);
}
}
}

View File

@ -15,14 +15,14 @@
</a>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span>
<span [hidden]="originScheduleType!==SCHEDULE_TYPE.CUSTOM">{{ oriCron }}</span>
<button class="btn btn-outline btn-sm" (click)="editSchedule()">
<button class="btn btn-outline btn-sm" (click)="editSchedule()" id="editSchedule">
{{ "BUTTON.EDIT" | translate }}
</button>
</div>
<div class="setting-wrapper flex-layout" *ngIf="isEditMode">
<span class="font-style">{{ labelEdit | translate }}</span>
<div class="select select-schedule">
<select name="gcPolicy" [(ngModel)]="scheduleType">
<select name="selectPolicy" id="selectPolicy" [(ngModel)]="scheduleType">
<option [value]="SCHEDULE_TYPE.NONE">{{'SCHEDULE.NONE' | translate}}</option>
<option [value]="SCHEDULE_TYPE.HOURLY">{{'SCHEDULE.HOURLY' | translate}}</option>
<option [value]="SCHEDULE_TYPE.DAILY">{{'SCHEDULE.DAILY' | translate}}</option>
@ -31,9 +31,9 @@
</select>
</div>
<span [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">{{ "SCHEDULE.CRON" | translate }} :</span>
<div [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM" >
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="cronStringInput.invalid && (cronStringInput.dirty || cronStringInput.touched)" class="tooltip tooltip-validation tooltip-md tooltip-top-right">
<input type="text" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="form-control cron-input"
<div class="form-group" [hidden]="scheduleType!==SCHEDULE_TYPE.CUSTOM">
<label for="targetCron" aria-haspopup="true" role="tooltip" [class.invalid]="cronStringInput.invalid && (cronStringInput.dirty || cronStringInput.touched)" class="tooltip tooltip-validation tooltip-md tooltip-top-right cron-label">
<input type="text" name=targetCron id="targetCron" #cronStringInput="ngModel" required class="form-control"
[(ngModel)]="cronString">
<span class="tooltip-content">
{{'TOOLTIP.CRON_REQUIRED' | translate }}
@ -41,10 +41,10 @@
</label>
</div>
<button class="btn btn-primary btn-sm"
(click)="save()">
(click)="save()" id="config-save">
{{ "BUTTON.SAVE" | translate }}
</button>
<button class="btn btn-primary btn-sm" (click)="isEditMode= false">
<button class="btn btn-primary btn-sm" (click)="isEditMode=false">
{{ "BUTTON.CANCEL" | translate }}
</button>
</div>

View File

@ -34,8 +34,8 @@
display: inline-block;
width: 100px;
}
.cron-input {
.cron-label {
width: 195px;
}
}

View File

@ -58,8 +58,10 @@ import {
UserPermissionDefaultService
} from './service/index';
import { GcRepoService } from './config/gc/gc.service';
import { ScanAllRepoService } from './config/vulnerability/scanAll.service';
import {GcViewModelFactory} from './config/gc/gc.viewmodel.factory';
import {GcApiRepository, GcApiDefaultRepository} from './config/gc/gc.api.repository';
import {ScanApiRepository, ScanApiDefaultRepository} from './config/vulnerability/scanAll.api.repository';
import {
ErrorHandler,
DefaultErrorHandler
@ -100,7 +102,8 @@ export const DefaultServiceConfig: IServiceConfig = {
labelEndpoint: "/api/labels",
helmChartEndpoint: "/api/chartrepo",
downloadChartEndpoint: "/chartrepo",
gcEndpoint: "/api/system/gc"
gcEndpoint: "/api/system/gc",
ScanAllEndpoint: "/api/system/scanAll"
};
/**
@ -160,6 +163,9 @@ export interface HarborModuleConfig {
// Service implementation for gc
gcApiRepository?: Provider;
// Service implementation for scanAll
ScanApiRepository?: Provider;
}
/**
@ -261,6 +267,7 @@ export class HarborLibraryModule {
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
config.gcApiRepository || {provide: GcApiRepository, useClass: GcApiDefaultRepository},
config.ScanApiRepository || {provide: ScanApiRepository, useClass: ScanApiDefaultRepository},
// Do initializing
TranslateServiceInitializer,
{
@ -272,6 +279,7 @@ export class HarborLibraryModule {
ChannelService,
OperationService,
GcRepoService,
ScanAllRepoService,
GcViewModelFactory
]
};
@ -297,9 +305,11 @@ export class HarborLibraryModule {
config.labelService || { provide: LabelService, useClass: LabelDefaultService },
config.userPermissionService || { provide: UserPermissionService, useClass: UserPermissionDefaultService },
config.gcApiRepository || {provide: GcApiRepository, useClass: GcApiDefaultRepository},
config.ScanApiRepository || {provide: ScanApiRepository, useClass: ScanApiDefaultRepository},
ChannelService,
OperationService,
GcRepoService,
ScanAllRepoService,
GcViewModelFactory
]
};

View File

@ -239,4 +239,6 @@ export interface IServiceConfig {
downloadChartEndpoint?: string;
gcEndpoint?: string;
ScanAllEndpoint?: string;
}

View File

@ -36,7 +36,7 @@
<button id="config-vulnerability" clrTabLink>{{'CONFIG.VULNERABILITY' | translate }}</button>
<ng-template [(clrIfActive)]="vulnerabilityActive">
<clr-tab-content id="vulnerability" *ngIf="withClair">
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
<vulnerability-config></vulnerability-config>
</clr-tab-content>
</ng-template>
</clr-tab>

View File

@ -75,7 +75,8 @@ const uiLibConfig: IServiceConfig = {
labelEndpoint: "/api/labels",
helmChartEndpoint: "/api/chartrepo",
downloadChartEndpoint: "/chartrepo",
gcEndpoint: "/api/system/gc"
gcEndpoint: "/api/system/gc",
ScanAllEndpoint: "/api/system/scanAll"
};
@NgModule({

View File

@ -23,7 +23,7 @@ ${config_save_button_xpath} //config//div/button[contains(.,'SAVE')]
${config_email_save_button_xpath} //*[@id='config_email_save']
${config_auth_save_button_xpath} //*[@id='config_auth_save']
${config_system_save_button_xpath} //*[@id='config_system_save']
${vulnerbility_save_button_xpath} //*[@id='config_vulnerbility_save']
${vulnerbility_save_button_xpath} //*[@id='config-save']
${configuration_xpath} //clr-vertical-nav-group-children/a[contains(.,'Configuration')]
${system_config_xpath} //*[@id='config-system']
${garbage_collection_xpath} //*[@id='config-gc']

View File

@ -7,10 +7,10 @@ Resource ../../resources/Util.robot
*** Keywords ***
Disable Scan Schedule
Click Element //vulnerability-config//button[@id='editSchedule']
Click Element //vulnerability-config//select[@id='scanAllPolicy']
Click Element //vulnerability-config//select[@id='scanAllPolicy']//option[contains(.,'None')]
Click Element //button[@id='config_vulnerbility_save']
Click Element //vulnerability-config//cron-selection//button[contains(.,'EDIT')]
Click Element //vulnerability-config//cron-selection//select[@id='selectPolicy']
Click Element //vulnerability-config//cron-selection//select[@id='selectPolicy']//option[contains(.,'None')]
Click Element //cron-selection//button[contains(.,'SAVE')]
Go To Vulnerability Config
Click Element //config//button[contains(.,'Vulnerability')]