Add stop button for audit log rotation (#17054)

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2022-06-28 15:48:17 +08:00 committed by GitHub
parent 25d3583d36
commit 889407ab38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 185 additions and 26 deletions

View File

@ -4,7 +4,22 @@
<span class="refresh-btn" (click)="refresh()"> <span class="refresh-btn" (click)="refresh()">
<clr-icon shape="refresh"></clr-icon> <clr-icon shape="refresh"></clr-icon>
</span> </span>
<clr-datagrid [clrDgLoading]="loading" (clrDgRefresh)="getJobs(true, $event)"> <clr-datagrid
[(clrDgSelected)]="selectedRow"
[clrDgLoading]="loading"
(clrDgRefresh)="getJobs(true, $event)">
<clr-dg-action-bar>
<div class="btn-group">
<button
id="stop-purge"
type="button"
class="btn btn-secondary"
[disabled]="!canStop()"
(click)="openStopExecutionsDialog()">
{{ 'REPLICATION.STOPJOB' | translate }}
</button>
</div>
</clr-dg-action-bar>
<clr-dg-column [clrDgField]="'id'">{{ <clr-dg-column [clrDgField]="'id'">{{
'GC.JOB_ID' | translate 'GC.JOB_ID' | translate
}}</clr-dg-column> }}</clr-dg-column>

View File

@ -13,6 +13,7 @@ import { delay } from 'rxjs/operators';
import { PurgeHistoryComponent } from './purge-history.component'; import { PurgeHistoryComponent } from './purge-history.component';
import { ExecHistory } from '../../../../../../../ng-swagger-gen/models/exec-history'; import { ExecHistory } from '../../../../../../../ng-swagger-gen/models/exec-history';
import { PurgeService } from 'ng-swagger-gen/services/purge.service'; import { PurgeService } from 'ng-swagger-gen/services/purge.service';
import { ConfirmationDialogService } from '../../../../global-confirmation-dialog/confirmation-dialog.service';
describe('GcHistoryComponent', () => { describe('GcHistoryComponent', () => {
let component: PurgeHistoryComponent; let component: PurgeHistoryComponent;
@ -65,11 +66,18 @@ describe('GcHistoryComponent', () => {
} }
}, },
}; };
const fakedConfirmationDialogService = {
openComfirmDialog() {},
};
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [PurgeHistoryComponent], declarations: [PurgeHistoryComponent],
imports: [SharedTestingModule], imports: [SharedTestingModule],
providers: [ providers: [
{
provide: ConfirmationDialogService,
useValue: fakedConfirmationDialogService,
},
{ provide: PurgeService, useValue: fakedPurgeService }, { provide: PurgeService, useValue: fakedPurgeService },
// open auto detect // open auto detect
{ provide: ComponentFixtureAutoDetect, useValue: true }, { provide: ComponentFixtureAutoDetect, useValue: true },
@ -103,4 +111,14 @@ describe('GcHistoryComponent', () => {
`${CURRENT_BASE_HREF}/system/purgeaudit/1/log` `${CURRENT_BASE_HREF}/system/purgeaudit/1/log`
); );
}); });
it('stopping purging should work', () => {
const sy: jasmine.Spy = spyOn(
fakedConfirmationDialogService,
'openComfirmDialog'
).and.returnValue(undefined);
const stopBtn: HTMLButtonElement =
fixture.nativeElement.querySelector('#stop-purge');
stopBtn.dispatchEvent(new Event('click'));
expect(sy.calls.count()).toEqual(1);
});
}); });

View File

@ -1,8 +1,12 @@
import { Component, OnDestroy } from '@angular/core'; import { Component, OnDestroy, OnInit } from '@angular/core';
import { ClrDatagridStateInterface } from '@clr/angular'; import { ClrDatagridStateInterface } from '@clr/angular';
import { GCHistory } from 'ng-swagger-gen/models/gchistory'; import { finalize, forkJoin, Subscription, timer } from 'rxjs';
import { finalize, Subscription, timer } from 'rxjs'; import {
import { REFRESH_TIME_DIFFERENCE } from 'src/app/shared/entities/shared.const'; ConfirmationButtons,
ConfirmationState,
ConfirmationTargets,
REFRESH_TIME_DIFFERENCE,
} from 'src/app/shared/entities/shared.const';
import { ErrorHandler } from 'src/app/shared/units/error-handler/error-handler'; import { ErrorHandler } from 'src/app/shared/units/error-handler/error-handler';
import { import {
CURRENT_BASE_HREF, CURRENT_BASE_HREF,
@ -13,14 +17,17 @@ import {
} from 'src/app/shared/units/utils'; } from 'src/app/shared/units/utils';
import { PurgeService } from '../../../../../../../ng-swagger-gen/services/purge.service'; import { PurgeService } from '../../../../../../../ng-swagger-gen/services/purge.service';
import { JOB_STATUS, NO, YES } from '../../clearing-job-interfact'; import { JOB_STATUS, NO, YES } from '../../clearing-job-interfact';
import { ConfirmationMessage } from '../../../../global-confirmation-dialog/confirmation-message';
import { ConfirmationDialogService } from '../../../../global-confirmation-dialog/confirmation-dialog.service';
import { ExecHistory } from '../../../../../../../ng-swagger-gen/models/exec-history';
@Component({ @Component({
selector: 'app-purge-history', selector: 'app-purge-history',
templateUrl: './purge-history.component.html', templateUrl: './purge-history.component.html',
styleUrls: ['./purge-history.component.scss'], styleUrls: ['./purge-history.component.scss'],
}) })
export class PurgeHistoryComponent implements OnDestroy { export class PurgeHistoryComponent implements OnInit, OnDestroy {
jobs: Array<GCHistory> = []; jobs: Array<ExecHistory> = [];
loading: boolean = true; loading: boolean = true;
timerDelay: Subscription; timerDelay: Subscription;
pageSize: number = getPageSizeFromLocalStorage( pageSize: number = getPageSizeFromLocalStorage(
@ -30,10 +37,61 @@ export class PurgeHistoryComponent implements OnDestroy {
page: number = 1; page: number = 1;
total: number = 0; total: number = 0;
state: ClrDatagridStateInterface; state: ClrDatagridStateInterface;
selectedRow: ExecHistory[] = [];
isStopOnGoing: boolean = false;
subscription: Subscription;
constructor( constructor(
private purgeService: PurgeService, private purgeService: PurgeService,
private errorHandler: ErrorHandler private errorHandler: ErrorHandler,
private confirmationDialogService: ConfirmationDialogService
) {} ) {}
ngOnInit() {
if (!this.subscription) {
this.subscription =
this.confirmationDialogService.confirmationConfirm$.subscribe(
message => {
if (
message &&
message.state === ConfirmationState.CONFIRMED &&
message.source ===
ConfirmationTargets.STOP_AUDIT_LOG_ROTATION
) {
this.stopRotation(message.data);
}
}
);
}
}
ngOnDestroy() {
if (this.timerDelay) {
this.timerDelay.unsubscribe();
this.timerDelay = null;
}
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = null;
}
}
stopRotation(execHistories: ExecHistory[]) {
this.isStopOnGoing = true;
forkJoin(
execHistories.map(item => {
return this.purgeService.stopPurge({
purgeId: item.id,
});
})
)
.pipe(finalize(() => (this.isStopOnGoing = false)))
.subscribe({
next: res => {
this.errorHandler.info('CLEARANCES.STOP_PURGE_SUCCESS');
},
error: err => {
this.errorHandler.error(err);
},
});
}
refresh() { refresh() {
this.page = 1; this.page = 1;
this.total = 0; this.total = 0;
@ -81,7 +139,22 @@ export class PurgeHistoryComponent implements OnDestroy {
if (xHeader) { if (xHeader) {
this.total = parseInt(xHeader, 0); this.total = parseInt(xHeader, 0);
} }
this.jobs = res.body; if (!withLoading) {
if (res?.body?.length) {
res.body.forEach(item => {
this.jobs.forEach(item2 => {
if (item2.id === item.id) {
item2.job_status = item.job_status;
item2.update_time =
item.update_time;
}
});
});
}
} else {
this.selectedRow = [];
this.jobs = res.body;
}
} }
// to avoid some jobs not finished. // to avoid some jobs not finished.
if (!this.timerDelay) { if (!this.timerDelay) {
@ -124,14 +197,36 @@ export class PurgeHistoryComponent implements OnDestroy {
return NO; return NO;
} }
ngOnDestroy() {
if (this.timerDelay) {
this.timerDelay.unsubscribe();
this.timerDelay = null;
}
}
getLogLink(id): string { getLogLink(id): string {
return `${CURRENT_BASE_HREF}/system/purgeaudit/${id}/log`; return `${CURRENT_BASE_HREF}/system/purgeaudit/${id}/log`;
} }
canStop(): boolean {
if (this.isStopOnGoing) {
return false;
}
if (this.selectedRow?.length) {
return (
this.selectedRow.filter(item => {
return (
item.job_status === JOB_STATUS.PENDING ||
item.job_status === JOB_STATUS.RUNNING
);
})?.length > 0
);
}
return false;
}
openStopExecutionsDialog() {
const executionIds = this.selectedRow.map(robot => robot.id).join(',');
let StopExecutionsMessage = new ConfirmationMessage(
'REPLICATION.STOP_TITLE',
'REPLICATION.STOP_SUMMARY',
executionIds,
this.selectedRow,
ConfirmationTargets.STOP_AUDIT_LOG_ROTATION,
ConfirmationButtons.CONFIRM_CANCEL
);
this.confirmationDialogService.openComfirmDialog(StopExecutionsMessage);
}
} }

View File

@ -54,8 +54,8 @@
<form #purgeForm="ngForm" class="clr-form clr-form-horizontal p-0"> <form #purgeForm="ngForm" class="clr-form clr-form-horizontal p-0">
<div class="clr-form-control mt-0"> <div class="clr-form-control mt-0">
<span class="required font-style flex-200" <span class="required font-style flex-200"
>{{ 'CLEARANCES.KEEP_IN' | translate }} >{{ 'CLEARANCES.KEEP_IN' | translate
<clr-tooltip> }}<clr-tooltip>
<clr-icon <clr-icon
clrTooltipTrigger clrTooltipTrigger
shape="info-circle" shape="info-circle"

View File

@ -11,6 +11,7 @@
<clr-dg-action-bar> <clr-dg-action-bar>
<div class="btn-group"> <div class="btn-group">
<button <button
id="stop-gc"
type="button" type="button"
class="btn btn-secondary" class="btn btn-secondary"
[disabled]="!canStop()" [disabled]="!canStop()"

View File

@ -14,6 +14,7 @@ import { Registry } from '../../../../../../../../ng-swagger-gen/models/registry
import { GcService } from '../../../../../../../../ng-swagger-gen/services/gc.service'; import { GcService } from '../../../../../../../../ng-swagger-gen/services/gc.service';
import { CURRENT_BASE_HREF } from '../../../../../../shared/units/utils'; import { CURRENT_BASE_HREF } from '../../../../../../shared/units/utils';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
import { ConfirmationDialogService } from '../../../../../global-confirmation-dialog/confirmation-dialog.service';
describe('GcHistoryComponent', () => { describe('GcHistoryComponent', () => {
let component: GcHistoryComponent; let component: GcHistoryComponent;
@ -65,12 +66,22 @@ describe('GcHistoryComponent', () => {
return of(response).pipe(delay(0)); return of(response).pipe(delay(0));
} }
}, },
stopGC() {
return of(null);
},
};
const fakedConfirmationDialogService = {
openComfirmDialog() {},
}; };
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [GcHistoryComponent], declarations: [GcHistoryComponent],
imports: [SharedTestingModule], imports: [SharedTestingModule],
providers: [ providers: [
{
provide: ConfirmationDialogService,
useValue: fakedConfirmationDialogService,
},
{ provide: GcService, useValue: fakedGcService }, { provide: GcService, useValue: fakedGcService },
// open auto detect // open auto detect
{ provide: ComponentFixtureAutoDetect, useValue: true }, { provide: ComponentFixtureAutoDetect, useValue: true },
@ -104,4 +115,14 @@ describe('GcHistoryComponent', () => {
`${CURRENT_BASE_HREF}/system/gc/1/log` `${CURRENT_BASE_HREF}/system/gc/1/log`
); );
}); });
it('stopping GC should work', () => {
const sy: jasmine.Spy = spyOn(
fakedConfirmationDialogService,
'openComfirmDialog'
).and.returnValue(undefined);
const stopBtn: HTMLButtonElement =
fixture.nativeElement.querySelector('#stop-gc');
stopBtn.dispatchEvent(new Event('click'));
expect(sy.calls.count()).toEqual(1);
});
}); });

View File

@ -55,6 +55,7 @@ export const enum ConfirmationTargets {
ACCESSORY, ACCESSORY,
ALL_ACCESSORIES, ALL_ACCESSORIES,
STOP_GC, STOP_GC,
STOP_AUDIT_LOG_ROTATION,
} }
export const enum ActionType { export const enum ActionType {

View File

@ -1751,6 +1751,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1751,6 +1751,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1750,6 +1750,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1720,6 +1720,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1747,6 +1747,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1751,6 +1751,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }

View File

@ -1749,6 +1749,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "将日志转发到指定的 syslog 端点例如harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "将日志转发到指定的 syslog 端点例如harbor-log:10514",
"SKIP_DATABASE": "跳过日志数据库", "SKIP_DATABASE": "跳过日志数据库",
"SKIP_DATABASE_TOOLTIP": "开启此项将不会在数据库中记录日志,需先配置日志转发端点", "SKIP_DATABASE_TOOLTIP": "开启此项将不会在数据库中记录日志,需先配置日志转发端点",
"STOP_GC_SUCCESS": "成功触发停止垃圾回收的操作" "STOP_GC_SUCCESS": "成功触发停止垃圾回收的操作",
"STOP_PURGE_SUCCESS": "成功触发停止清理日志的操作"
} }
} }

View File

@ -1742,6 +1742,7 @@
"FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514", "FORWARD_ENDPOINT_TOOLTIP": "Forward audit logs to the syslog endpoint, for example: harbor-log:10514",
"SKIP_DATABASE": "Skip Audit Log Database", "SKIP_DATABASE": "Skip Audit Log Database",
"SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured", "SKIP_DATABASE_TOOLTIP": "Skip to log audit log in the database, only available when audit log forward endpoint is configured",
"STOP_GC_SUCCESS": "Trigger stop GC operation successfully" "STOP_GC_SUCCESS": "Trigger stopping GC operation successfully",
"STOP_PURGE_SUCCESS": "Trigger stopping purging operation successfully"
} }
} }