diff --git a/src/portal/lib/README.md b/src/portal/lib/README.md index 80139a294..e22755b90 100644 --- a/src/portal/lib/README.md +++ b/src/portal/lib/README.md @@ -258,9 +258,9 @@ export const DefaultServiceConfig: IServiceConfig = { systemInfoEndpoint: "/api/systeminfo", repositoryBaseEndpoint: "/api/repositories", logBaseEndpoint: "/api/logs", - targetBaseEndpoint: "/api/targets", + targetBaseEndpoint: "/api/registries", replicationRuleEndpoint: "/api/policies/replication", - replicationJobEndpoint: "/api/jobs/replication", + replicationBaseEndpoint: "/api/replication/executions", vulnerabilityScanningBaseEndpoint: "/api/repositories", configurationEndpoint: "/api/configurations", enablei18Support: false, @@ -293,11 +293,11 @@ It supports partially overriding. For the items not overridden, default values w * **logBaseEndpoint:** The base endpoint of the service used to handle the recent access logs. Default is "/api/logs". -* **targetBaseEndpoint:** The base endpoint of the service used to handle the registry endpoints. Default is "/api/targets". +* **targetBaseEndpoint:** The base endpoint of the service used to handle the registry endpoints. Default is "/api/registries". * **replicationRuleEndpoint:** The base endpoint of the service used to handle the replication rules. Default is "/api/policies/replication". -* **replicationJobEndpoint:** The base endpoint of the service used to handle the replication jobs. Default is "/api/jobs/replication". +* **replicationBaseEndpoint:** The base endpoint of the service used to handle the replication executions. Default is "/api/replication/executions". * **vulnerabilityScanningBaseEndpoint:** The base endpoint of the service used to handle the vulnerability scanning results.Default value is "/api/repositories". diff --git a/src/portal/lib/src/confirmation-dialog/confirmation-dialog.component.html b/src/portal/lib/src/confirmation-dialog/confirmation-dialog.component.html index 520a97f11..5daa0b16f 100644 --- a/src/portal/lib/src/confirmation-dialog/confirmation-dialog.component.html +++ b/src/portal/lib/src/confirmation-dialog/confirmation-dialog.component.html @@ -26,5 +26,9 @@ + + + + \ No newline at end of file diff --git a/src/portal/lib/src/create-edit-rule/create-edit-rule.component.spec.ts b/src/portal/lib/src/create-edit-rule/create-edit-rule.component.spec.ts index da1948f78..b78496707 100644 --- a/src/portal/lib/src/create-edit-rule/create-edit-rule.component.spec.ts +++ b/src/portal/lib/src/create-edit-rule/create-edit-rule.component.spec.ts @@ -40,6 +40,7 @@ import { OperationService } from "../operation/operation.service"; import {FilterLabelComponent} from "./filter-label.component"; import {LabelService} from "../service/label.service"; import {LabelPieceComponent} from "../label-piece/label-piece.component"; +import { RouterTestingModule } from '@angular/router/testing'; describe("CreateEditRuleComponent (inline template)", () => { let mockRules: ReplicationRule[] = [ @@ -226,13 +227,13 @@ describe("CreateEditRuleComponent (inline template)", () => { let spyEndpoint: jasmine.Spy; let config: IServiceConfig = { - replicationJobEndpoint: "/api/jobs/replication/testing", + replicationBaseEndpoint: "/api/replication/executions/testing", targetBaseEndpoint: "/api/targets/testing" }; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [SharedModule, NoopAnimationsModule], + imports: [SharedModule, NoopAnimationsModule, RouterTestingModule], declarations: [ ReplicationComponent, ListReplicationRuleComponent, @@ -277,7 +278,7 @@ describe("CreateEditRuleComponent (inline template)", () => { replicationService, "getReplicationRule" ).and.returnValue(Promise.resolve(mockRule)); - spyJobs = spyOn(replicationService, "getJobs").and.returnValues( + spyJobs = spyOn(replicationService, "getExecutions").and.returnValues( Promise.resolve(mockJob) ); diff --git a/src/portal/lib/src/harbor-library.module.ts b/src/portal/lib/src/harbor-library.module.ts index 44baa557c..82bbe4d00 100644 --- a/src/portal/lib/src/harbor-library.module.ts +++ b/src/portal/lib/src/harbor-library.module.ts @@ -83,10 +83,9 @@ export const DefaultServiceConfig: IServiceConfig = { systemInfoEndpoint: "/api/systeminfo", repositoryBaseEndpoint: "/api/repositories", logBaseEndpoint: "/api/logs", - targetBaseEndpoint: "/api/targets", - replicationBaseEndpoint: "/api/replications", - replicationRuleEndpoint: "/api/policies/replication", - replicationJobEndpoint: "/api/jobs/replication", + targetBaseEndpoint: "/api/registries", + replicationBaseEndpoint: "/api/replication/executions", + replicationRuleEndpoint: "/api/replication/policies", vulnerabilityScanningBaseEndpoint: "/api/repositories", projectPolicyEndpoint: "/api/projects/configs", projectBaseEndpoint: "/api/projects", diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html index 83188bda5..26d93e331 100644 --- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html +++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.html @@ -7,28 +7,32 @@ {{'REPLICATION.NAME' | translate}} - {{'REPLICATION.STATUS' | translate}} - {{'REPLICATION.PROJECT' | translate}} + {{'REPLICATION.SRC_NAMESPACE' | translate}} + {{'REPLICATION.REPLICATION_MODE' | translate}} + {{'REPLICATION.DESTINATION_NAMESPACE' | translate}} + {{'REPLICATION.LAST_REPLICATION' | translate}} + {{'REPLICATION.REPLICATION_TRIGGER' | translate}} {{'REPLICATION.DESCRIPTION' | translate}} - {{'REPLICATION.DESTINATION_NAME' | translate}} - {{'REPLICATION.TRIGGER_MODE' | translate}} {{'REPLICATION.PLACEHOLDER' | translate }} {{p.name}} + + {{p.src_registry_id>0? srcRegistry : 'current'}} : {{p.src_namespaces?.length>0 ? p.src_namespaces[0]: ''}} + + + + {{p.src_namespaces}} + + + -
- - Disabled - - {{'REPLICATION.RULE_DISABLED' | translate}} - - -
Enabled
-
+ {{p.src_registry_id>0? 'pull-based' : 'push-based'}}
- - {{p.projects?.length>0 ? p.projects[0].name : ''}} + + {{p.dest_registry_id>0? srcRegistry : 'current'}} : {{p.dest_namespace? p.dest_namespace: ''}} + {{p.update_time | date: 'short'}} + {{p.trigger ? p.trigger.type : ''}} {{p.description ? trancatedDescription(p.description) : '-'}} @@ -38,8 +42,6 @@ - {{p.targets?.length>0 ? p.targets[0].name : ''}} - {{p.trigger ? p.trigger.kind : ''}}
{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} {{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}} diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss index e38cca005..df6c5371f 100644 --- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss +++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.scss @@ -4,4 +4,7 @@ .text-alignment { vertical-align: text-bottom; -} \ No newline at end of file +} +.min-width { + width: 224px; +} diff --git a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts index 91c8820c9..4b9edda72 100644 --- a/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts +++ b/src/portal/lib/src/list-replication-rule/list-replication-rule.component.ts @@ -82,6 +82,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges { changedRules: ReplicationRule[]; ruleName: string; canDeleteRule: boolean; + srcRegistry: string = 'docker hub'; selectedRow: ReplicationRule; @@ -201,7 +202,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges { let ruleData: ReplicationJobItem[]; this.canDeleteRule = true; let count = 0; - return toPromise(this.replicationService.getJobs(id)) + return toPromise(this.replicationService.getExecutions(id)) .then(response => { ruleData = response.data; if (ruleData.length) { diff --git a/src/portal/lib/src/replication/index.ts b/src/portal/lib/src/replication/index.ts index 73d5c2433..b03d00e6f 100644 --- a/src/portal/lib/src/replication/index.ts +++ b/src/portal/lib/src/replication/index.ts @@ -1,8 +1,11 @@ import { Type } from '@angular/core'; import { ReplicationComponent } from './replication.component'; +import { ReplicationTasksComponent } from './replication-tasks/replication-tasks.component'; export * from './replication.component'; +export * from './replication-tasks/replication-tasks.component'; export const REPLICATION_DIRECTIVES: Type[] = [ - ReplicationComponent + ReplicationComponent, + ReplicationTasksComponent ]; diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html new file mode 100644 index 000000000..1b659ef8f --- /dev/null +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.html @@ -0,0 +1,117 @@ +
+
+
+
+ +
+
+

{{'REPLICATION.REPLICATION_EXECUTION'| translate}}

+

{{executionId}}

+
+
+ In Progress + Success + Failture +
+
+
+
+ +
+
+
+
+
+
+
+ {{'REPLICATION.SUCCESS'| translate}} +
{{'1'}}
+
+
+ {{'REPLICATION.FAILTURE'| translate}} +
{{'2'}}
+
+
+ {{'REPLICATION.IN_PROGRESS'| translate}} +
{{'3'}}
+
+
+
+
+
+
+
+ + {{'My 1st policy'}} +
+
+ + {{'Schedule'}} +
+
+ + {{'3/14/19, 2:26 PM'}} +
+
+
+
+
+ +
+ +
+
+
+ +
+ + + + + +
+
+ + {{'REPLICATION.TASK_ID'| translate}} + {{'REPLICATION.RECOURCE_TYPE' | translate}} + {{'REPLICATION.RECOURCE' | translate}} + {{'REPLICATION.DESTINATION' | translate}} + {{'REPLICATION.STATUS' | translate}} + {{'REPLICATION.CREATION_TIME' | translate}} + {{'REPLICATION.END_TIME' | translate}} + {{'REPLICATION.LOGS' | translate}} + + {{t.id}} + {{t.resource_type}} + {{t.src_resource}} + {{t.dst_resource}} + {{t.status}} + {{t.start_time | date: 'short'}} + {{t.end_time | date: 'short'}} + + {{'REPLICATION.NO_LOGS' | translate}} + + + + + + + + + {{pagination.firstItem + 1}} + - + {{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' | + translate}} + {{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}} + + + +
+
\ No newline at end of file diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss new file mode 100644 index 000000000..30aca0902 --- /dev/null +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.scss @@ -0,0 +1,63 @@ +.replication-tasks { + .overview-section { + .title-wrapper { + .onback{ + color: #007cbb; + font-size: 12px; + cursor: pointer; + } + h4 { + margin-top: 8px; + } + } + .execution-block { + margin-top: 24px; + display: flex; + flex-wrap: row wrap; + .execution-detail-label { + margin-right: 10px; + text-align: left; + .detail-row { + display: flex; + .detail-span { + flex:0 0 100px; + font-weight: 600; + margin-top: 10px; + } + .execution-details { + width: 200px; + margin: 8px 35px; + } + } + } + .executions-detail { + font-weight: 600; + span { + margin-left: 5px; + } + } + } + } + .tasks-detail { + margin-top: 65px; + .action-select { + padding-right: 18px; + height: 24px; + display: flex; + justify-content: flex-end; + + .filterTag { + float: left; + margin-top: 8px; + } + .refresh-btn { + cursor: pointer; + margin-top: 7px; + } + + .refresh-btn:hover { + color: #007CBB; + } + } + } +} \ No newline at end of file diff --git a/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts new file mode 100644 index 000000000..eedbc58ff --- /dev/null +++ b/src/portal/lib/src/replication/replication-tasks/replication-tasks.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Router } from '@angular/router'; +import { ReplicationService } from "../../service/replication.service"; +import { map, catchError } from "rxjs/operators"; +import { Observable, forkJoin, throwError as observableThrowError } from "rxjs"; +import { ErrorHandler } from "../../error-handler/error-handler"; +import { ReplicationJob, ReplicationTasks, Comparator, ReplicationJobItem } from "../../service/interface"; +import { CustomComparator } from "../../utils"; +@Component({ + selector: 'replication-tasks', + templateUrl: './replication-tasks.component.html', + styleUrls: ['./replication-tasks.component.scss'] +}) +export class ReplicationTasksComponent implements OnInit { + isOpenFilterTag: boolean; + selectedRow: []; + tasks: ReplicationTasks[] = []; + stopOnGoing: boolean; + executions: string = 'InProgress'; + @Input() executionId: string; + startTimeComparator: Comparator = new CustomComparator< + ReplicationJob + >("start_time", "date"); + endTimeComparator: Comparator = new CustomComparator< + ReplicationJob + >("end_time", "date"); + + constructor( + private router: Router, + private replicationService: ReplicationService, + private errorHandler: ErrorHandler, + ) { } + + ngOnInit(): void { + // this.getExecutions(); + this.getTasks(); + // this.executions.status = 'success'; + } + + // getExecutions(): void { + // if (this.executionId) { + // toPromise( + // this.replicationService.getExecutions(this.executionId) + // ) + // .then(executions => { + // console.log(executions); + // }) + // .catch(error => { + // this.errorHandler.error(error); + // }); + // } + // } + + stopJob() { + this.stopOnGoing = true; + this.replicationService.stopJobs(this.executionId) + .subscribe(res => { + this.stopOnGoing = false; + // this.getExecutions(); + }, + error => { + this.errorHandler.error(error); + }); + } + + viewLog(taskId: number | string): string { + return this.replicationService.getJobBaseUrl() + "/" + this.executionId + "/tasks/" + taskId + "/log"; + } + + getTasks(): void { + this.replicationService.getReplicationTasks(this.executionId) + .subscribe(tasks => { + this.tasks = tasks.map(x => Object.assign({}, x)); + }, + error => { + this.errorHandler.error(error); + }); + } + onBack(): void { + this.router.navigate(["harbor", "replications"]); + } + + openFilter(isOpen: boolean): void { + if (isOpen) { + this.isOpenFilterTag = true; + } else { + this.isOpenFilterTag = false; + } +} + +} diff --git a/src/portal/lib/src/replication/replication.component.html b/src/portal/lib/src/replication/replication.component.html index f4d9a2a6c..6a542907f 100644 --- a/src/portal/lib/src/replication/replication.component.html +++ b/src/portal/lib/src/replication/replication.component.html @@ -23,7 +23,7 @@
-
{{'REPLICATION.REPLICATION_JOBS' | translate}}
+
{{'REPLICATION.REPLICATION_EXECUTIONS' | translate}}
- +
- +
- {{'REPLICATION.NAME' | translate}} - {{'REPLICATION.STATUS' | translate}} - {{'REPLICATION.OPERATION' | translate}} + {{'REPLICATION.ID' | translate}} + {{'REPLICATION.REPLICATION_TRIGGER' | translate}} {{'REPLICATION.CREATION_TIME' | translate}} - {{'REPLICATION.UPDATE_TIME' | translate}} - {{'REPLICATION.LOGS' | translate}} + {{'REPLICATION.DURATION' | translate}} + {{'REPLICATION.SUCCESS_RATE' | translate}} + {{'REPLICATION.STATUS' | translate}} {{'REPLICATION.JOB_PLACEHOLDER' | translate }} - - {{j.repository}} - {{j.status}} - {{j.operation}} - {{j.creation_time | date: 'short'}} - {{j.update_time | date: 'short'}} + - {{'REPLICATION.NO_LOGS' | translate}} - - - - - + {{j.id}} + {{j.trigger}} + {{j.start_time | date: 'short'}} + {{'3mins'}} + + {{'90%'}} + + {{j.status}} {{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}} @@ -86,4 +83,5 @@ +
\ No newline at end of file diff --git a/src/portal/lib/src/replication/replication.component.spec.ts b/src/portal/lib/src/replication/replication.component.spec.ts index 018ac4452..25a32bbfd 100644 --- a/src/portal/lib/src/replication/replication.component.spec.ts +++ b/src/portal/lib/src/replication/replication.component.spec.ts @@ -22,6 +22,7 @@ import {ProjectDefaultService, ProjectService} from "../service/project.service" import {OperationService} from "../operation/operation.service"; import {FilterLabelComponent} from "../create-edit-rule/filter-label.component"; import {LabelPieceComponent} from "../label-piece/label-piece.component"; +import { RouterTestingModule } from '@angular/router/testing'; describe('Replication Component (inline template)', () => { @@ -208,14 +209,15 @@ describe('Replication Component (inline template)', () => { let config: IServiceConfig = { replicationRuleEndpoint: '/api/policies/replication/testing', - replicationJobEndpoint: '/api/jobs/replication/testing' + replicationBaseEndpoint: '/api/replication/executions/testing' }; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ SharedModule, - NoopAnimationsModule + NoopAnimationsModule, + RouterTestingModule ], declarations: [ ReplicationComponent, @@ -253,7 +255,7 @@ describe('Replication Component (inline template)', () => { endpointService = fixtureCreate.debugElement.injector.get(EndpointService); spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules)); - spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob)); + spyJobs = spyOn(replicationService, 'getExecutions').and.returnValues(Promise.resolve(mockJob)); spyEndpoint = spyOn(endpointService, 'getEndpoints').and.returnValues(Promise.resolve(mockEndpoints)); diff --git a/src/portal/lib/src/replication/replication.component.ts b/src/portal/lib/src/replication/replication.component.ts index ece3c95da..75da85ac1 100644 --- a/src/portal/lib/src/replication/replication.component.ts +++ b/src/portal/lib/src/replication/replication.component.ts @@ -21,8 +21,8 @@ import { EventEmitter } from "@angular/core"; import { Comparator, State } from "../service/interface"; -import { Subscription, forkJoin, timer} from "rxjs"; - +import { Subscription, forkJoin, timer, throwError} from "rxjs"; +import { finalize, catchError, map } from "rxjs/operators"; import { TranslateService } from "@ngx-translate/core"; @@ -58,6 +58,8 @@ import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation import {operateChanges, OperationState, OperateInfo} from "../operation/operate"; import {OperationService} from "../operation/operation.service"; +import { Router } from "@angular/router"; + const ruleStatus: { [key: string]: any } = [ { key: "all", description: "REPLICATION.ALL_STATUS" }, { key: "1", description: "REPLICATION.ENABLED" }, @@ -124,6 +126,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { changedRules: ReplicationRule[]; + selectedRow: ReplicationJobItem[] = []; rules: ReplicationRule[]; loading: boolean; isStopOnGoing: boolean; @@ -144,12 +147,15 @@ export class ReplicationComponent implements OnInit, OnDestroy { @ViewChild("replicationConfirmDialog") replicationConfirmDialog: ConfirmationDialogComponent; + @ViewChild("StopConfirmDialog") + StopConfirmDialog: ConfirmationDialogComponent; + creationTimeComparator: Comparator = new CustomComparator< ReplicationJob - >("creation_time", "date"); + >("start_time", "date"); updateTimeComparator: Comparator = new CustomComparator< ReplicationJob - >("update_time", "date"); + >("end_time", "date"); // Server driven pagination currentPage: number = 1; @@ -160,6 +166,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { timerDelay: Subscription; constructor( + private router: Router, private errorHandler: ErrorHandler, private replicationService: ReplicationService, private operationService: OperationService, @@ -197,6 +204,11 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.goToRegistry.emit(); } + goToLink(exeId: number): void { + let linkUrl = ["harbor", "replications", exeId, "tasks"]; + this.router.navigate(linkUrl); + } + // Server driven data loading clrLoadJobs(state: State): void { if (!state || !state.page || !this.search.ruleId) { @@ -237,7 +249,7 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.jobsLoading = false; toPromise( - this.replicationService.getJobs(this.search.ruleId, params) + this.replicationService.getExecutions(this.search.ruleId, params) ) .then(response => { this.totalCount = response.metadata.xTotalCount; @@ -394,18 +406,68 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.hiddenJobList = true; } - stopJobs() { - if (this.jobs && this.jobs.length) { - this.isStopOnGoing = true; - toPromise(this.replicationService.stopJobs(this.jobs[0].policy_id)) - .then(res => { - this.refreshJobs(); - this.isStopOnGoing = false; - }) - .catch(error => this.errorHandler.error(error)); + openStopExecutionsDialog(targets: ReplicationJobItem[]) { + let ExecutionId = targets.map(robot => robot.id).join(","); + let StopExecutionsMessage = new ConfirmationMessage( + "REPLICATION.STOP_TITLE", + "REPLICATION.STOP_SUMMARY", + ExecutionId, + targets, + ConfirmationTargets.STOP_EXECUTIONS, + ConfirmationButtons.STOP_CANCEL + ); + this.StopConfirmDialog.open(StopExecutionsMessage); + } + + confirmStop(message: ConfirmationAcknowledgement) { + if ( + message && + message.state === ConfirmationState.CONFIRMED && + message.source === ConfirmationTargets.STOP_EXECUTIONS + ) { + this.StopExecutions(message.data); } } + StopExecutions(targets: ReplicationJobItem[]): void { + if (targets && targets.length < 1) { + return; + } + + this.isStopOnGoing = true; + if (this.jobs && this.jobs.length) { + let ExecutionsStop$ = targets.map(target => this.StopOperate(target)); + forkJoin(ExecutionsStop$) + .pipe( + catchError(err => throwError(err)), + finalize(() => { + this.refreshJobs(); + this.isStopOnGoing = false; + this.selectedRow = []; + }) + ) + .subscribe(() => { }); + } + } + + StopOperate(targets: ReplicationJobItem): any { + let operMessage = new OperateInfo(); + operMessage.name = "OPERATION.STOP_EXECUTIONS"; + operMessage.data.id = targets.id; + operMessage.state = OperationState.progressing; + operMessage.data.name = targets.id; + this.operationService.publishInfo(operMessage); + + return this.replicationService + .stopJobs(targets.id) + .pipe( + map( + () => operateChanges(operMessage, OperationState.success), + err => operateChanges(operMessage, OperationState.failure, err) + ) + ); + } + reloadRules(isReady: boolean) { if (isReady) { this.search.ruleName = ""; @@ -453,8 +515,4 @@ export class ReplicationComponent implements OnInit, OnDestroy { this.search.endTimestamp = toTimestamp; this.loadFirstPage(); } - - viewLog(jobId: number | string): string { - return this.replicationService.getJobBaseUrl() + "/" + jobId + "/log"; - } } diff --git a/src/portal/lib/src/service.config.ts b/src/portal/lib/src/service.config.ts index 185d80d00..c82fd8ae4 100644 --- a/src/portal/lib/src/service.config.ts +++ b/src/portal/lib/src/service.config.ts @@ -66,16 +66,6 @@ export interface IServiceConfig { */ replicationRuleEndpoint?: string; - - /** - * The base endpoint of the service used to handle the replication jobs. - * - * - * * {string} - * @memberOf IServiceConfig - */ - replicationJobEndpoint?: string; - /** * The base endpoint of the service used to handle vulnerability scanning. * diff --git a/src/portal/lib/src/service/endpoint.service.ts b/src/portal/lib/src/service/endpoint.service.ts index f1ebc148f..7a9cabb02 100644 --- a/src/portal/lib/src/service/endpoint.service.ts +++ b/src/portal/lib/src/service/endpoint.service.ts @@ -132,7 +132,7 @@ export class EndpointDefaultService extends EndpointService { super(); this._endpointUrl = config.targetBaseEndpoint ? config.targetBaseEndpoint - : "/api/targets"; + : "/api/registries"; } public getEndpoints( diff --git a/src/portal/lib/src/service/interface.ts b/src/portal/lib/src/service/interface.ts index 91c34601a..eb1fc802c 100644 --- a/src/portal/lib/src/service/interface.ts +++ b/src/portal/lib/src/service/interface.ts @@ -103,14 +103,24 @@ export interface ReplicationRule extends Base { filters: Filter[]; replicate_existing_image_now?: boolean; replicate_deletion?: boolean; + // id?: number; + // name: string; + // description: string; + // src_registry_id: number; + // src_namespaces: []; + // dest_registry_id: number; + // dest_namespace: string; + // trigger: Trigger; + // filter: Filter[]; + // deletion: boolean; + // override: boolean; + // enabled: boolean; } export class Filter { - kind: string; - pattern: string; - constructor(kind: string, pattern: string) { - this.kind = kind; - this.pattern = pattern; + type: string; + constructor(type: string) { + this.type = type; } } @@ -146,6 +156,7 @@ export interface ReplicationJob { */ export interface ReplicationJobItem extends Base { [key: string]: any | any[]; + id: number; status: string; repository: string; policy_id: number; @@ -153,6 +164,22 @@ export interface ReplicationJobItem extends Base { tags: string; } +/** + * Interface for replication tasks item. + * + ** + * interface ReplicationTasks + */ +export interface ReplicationTasks extends Base { + [key: string]: any | any[]; + id: number; + execution_id: number; + resource_type: string; + src_resource: string; + dst_resource: string; + job_id: number; + status: string; +} /** * Interface for storing metadata of response. * diff --git a/src/portal/lib/src/service/job-log.service.spec.ts b/src/portal/lib/src/service/job-log.service.spec.ts index 9ee59dbf5..7440d0276 100644 --- a/src/portal/lib/src/service/job-log.service.spec.ts +++ b/src/portal/lib/src/service/job-log.service.spec.ts @@ -6,7 +6,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config'; describe('JobLogService', () => { const mockConfig: IServiceConfig = { - replicationJobEndpoint: "/api/jobs/replication/testing", + replicationBaseEndpoint: "/api/replication/executions/testing", scanJobEndpoint: "/api/jobs/scan/testing" }; @@ -33,7 +33,7 @@ describe('JobLogService', () => { it('should be initialized', inject([JobLogDefaultService], (service: JobLogService) => { expect(service).toBeTruthy(); - expect(config.replicationJobEndpoint).toEqual("/api/jobs/replication/testing"); + expect(config.replicationBaseEndpoint).toEqual("/api/replication/executions/testing"); expect(config.scanJobEndpoint).toEqual("/api/jobs/scan/testing"); })); }); diff --git a/src/portal/lib/src/service/job-log.service.ts b/src/portal/lib/src/service/job-log.service.ts index f44d780ca..90363916b 100644 --- a/src/portal/lib/src/service/job-log.service.ts +++ b/src/portal/lib/src/service/job-log.service.ts @@ -47,9 +47,9 @@ export class JobLogDefaultService extends JobLogService { @Inject(SERVICE_CONFIG) config: IServiceConfig ) { super(); - this._replicationJobBaseUrl = config.replicationJobEndpoint - ? config.replicationJobEndpoint - : "/api/jobs/replication"; + this._replicationJobBaseUrl = config.replicationBaseEndpoint + ? config.replicationBaseEndpoint + : "/api/replication/executions"; this._scanningJobBaseUrl = config.scanJobEndpoint ? config.scanJobEndpoint : "/api/jobs/scan"; diff --git a/src/portal/lib/src/service/replication.service.spec.ts b/src/portal/lib/src/service/replication.service.spec.ts index b5022e1cb..6c3fae9cc 100644 --- a/src/portal/lib/src/service/replication.service.spec.ts +++ b/src/portal/lib/src/service/replication.service.spec.ts @@ -7,7 +7,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config'; describe('ReplicationService', () => { const mockConfig: IServiceConfig = { replicationRuleEndpoint: "/api/policies/replication/testing", - replicationJobEndpoint: "/api/jobs/replication/testing" + replicationBaseEndpoint: "/api/replication/executions/testing" }; let config: IServiceConfig; @@ -38,6 +38,6 @@ describe('ReplicationService', () => { it('should inject the right config', () => { expect(config).toBeTruthy(); expect(config.replicationRuleEndpoint).toEqual("/api/policies/replication/testing"); - expect(config.replicationJobEndpoint).toEqual("/api/jobs/replication/testing"); + expect(config.replicationBaseEndpoint).toEqual("/api/replication/executions/testing"); }); }); diff --git a/src/portal/lib/src/service/replication.service.ts b/src/portal/lib/src/service/replication.service.ts index 6f72a7f63..9303d9354 100644 --- a/src/portal/lib/src/service/replication.service.ts +++ b/src/portal/lib/src/service/replication.service.ts @@ -1,6 +1,5 @@ import { Http } from "@angular/http"; import { Injectable, Inject } from "@angular/core"; -import { Observable } from "rxjs"; import { SERVICE_CONFIG, IServiceConfig } from "../service.config"; import { @@ -11,10 +10,12 @@ import { import { ReplicationJob, ReplicationRule, - ReplicationJobItem + ReplicationJobItem, + ReplicationTasks } from "./interface"; import { RequestQueryParams } from "./RequestQueryParams"; - +import { map, catchError } from "rxjs/operators"; +import { Observable, throwError as observableThrowError } from "rxjs"; /** * Define the service methods to handle the replication (rule and job) related things. * @@ -59,6 +60,18 @@ export abstract class ReplicationService { ruleId: number | string ): Observable | Promise | ReplicationRule; + + /** + * Get the specified replication task. + * + * @abstract + * returns {(Observable)} + * + * @memberOf ReplicationService + */ + abstract getReplicationTasks( + executionId: number | string + ): Observable; /** * Create new replication rule. * @@ -146,7 +159,7 @@ export abstract class ReplicationService { * * @memberOf ReplicationService */ - abstract getJobs( + abstract getExecutions( ruleId: number | string, queryParams?: RequestQueryParams ): Observable | Promise | ReplicationJob; @@ -165,7 +178,7 @@ export abstract class ReplicationService { abstract stopJobs( jobId: number | string - ): Observable | Promise | string; + ): Observable; abstract getJobBaseUrl(): string; } @@ -180,7 +193,6 @@ export abstract class ReplicationService { @Injectable() export class ReplicationDefaultService extends ReplicationService { _ruleBaseUrl: string; - _jobBaseUrl: string; _replicateUrl: string; constructor( @@ -190,13 +202,10 @@ export class ReplicationDefaultService extends ReplicationService { super(); this._ruleBaseUrl = config.replicationRuleEndpoint ? config.replicationRuleEndpoint - : "/api/policies/replication"; - this._jobBaseUrl = config.replicationJobEndpoint - ? config.replicationJobEndpoint - : "/api/jobs/replication"; + : "/api/replication/policies"; this._replicateUrl = config.replicationBaseEndpoint ? config.replicationBaseEndpoint - : "/api/replications"; + : "/api/replication/executions"; } // Private methods @@ -212,7 +221,7 @@ export class ReplicationDefaultService extends ReplicationService { } public getJobBaseUrl() { - return this._jobBaseUrl; + return this._replicateUrl; } public getReplicationRules( @@ -257,6 +266,19 @@ export class ReplicationDefaultService extends ReplicationService { .catch(error => Promise.reject(error)); } + public getReplicationTasks( + executionId: number | string + ): Observable { + if (!executionId) { + return observableThrowError("Bad argument"); + } + let url: string = `${this._replicateUrl}/${executionId}/tasks`; + return this.http + .get(url, HTTP_GET_OPTIONS) + .pipe(map (response => response.json() as ReplicationTasks) + , catchError(error => observableThrowError(error))); + } + public createReplicationRule( replicationRule: ReplicationRule ): Observable | Promise | any { @@ -352,7 +374,7 @@ export class ReplicationDefaultService extends ReplicationService { .catch(error => Promise.reject(error)); } - public getJobs( + public getExecutions( ruleId: number | string, queryParams?: RequestQueryParams ): Observable | Promise | ReplicationJob { @@ -366,7 +388,7 @@ export class ReplicationDefaultService extends ReplicationService { queryParams.set("policy_id", "" + ruleId); return this.http - .get(this._jobBaseUrl, buildHttpRequestOptions(queryParams)) + .get(this._replicateUrl, buildHttpRequestOptions(queryParams)) .toPromise() .then(response => { let result: ReplicationJob = { @@ -401,7 +423,7 @@ export class ReplicationDefaultService extends ReplicationService { return Promise.reject("Bad argument"); } - let logUrl = `${this._jobBaseUrl}/${jobId}/log`; + let logUrl = `${this._replicateUrl}/${jobId}/log`; return this.http .get(logUrl, HTTP_GET_OPTIONS) .toPromise() @@ -412,14 +434,16 @@ export class ReplicationDefaultService extends ReplicationService { public stopJobs( jobId: number | string ): Observable | Promise | any { + if (!jobId || jobId <= 0) { + return observableThrowError("Bad request argument."); + } + let requestUrl: string = `${this._replicateUrl}/${jobId}`; return this.http .put( - this._jobBaseUrl, - JSON.stringify({ policy_id: jobId, status: "stop" }), + requestUrl, HTTP_JSON_OPTIONS ) - .toPromise() - .then(response => response) - .catch(error => Promise.reject(error)); + .pipe(map(response => response) + , catchError(error => observableThrowError(error))); } } diff --git a/src/portal/lib/src/shared/shared.const.ts b/src/portal/lib/src/shared/shared.const.ts index f180073ad..ca088f502 100644 --- a/src/portal/lib/src/shared/shared.const.ts +++ b/src/portal/lib/src/shared/shared.const.ts @@ -43,7 +43,8 @@ export const enum ConfirmationTargets { CONFIG_ROUTE, CONFIG_TAB, HELM_CHART, - HELM_CHART_VERSION + HELM_CHART_VERSION, + STOP_EXECUTIONS } export const enum ActionType { @@ -69,7 +70,7 @@ export const enum ConfirmationState { } export const enum ConfirmationButtons { - CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, REPLICATE_CANCEL + CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, REPLICATE_CANCEL, STOP_CANCEL } export const LabelColor = [ diff --git a/src/portal/src/app/harbor-routing.module.ts b/src/portal/src/app/harbor-routing.module.ts index 4ec78ac7e..e7b3170e4 100644 --- a/src/portal/src/app/harbor-routing.module.ts +++ b/src/portal/src/app/harbor-routing.module.ts @@ -30,6 +30,8 @@ import { ResetPasswordComponent } from './account/password-setting/reset-passwor import { GroupComponent } from './group/group.component'; import { TotalReplicationPageComponent } from './replication/total-replication/total-replication-page.component'; +import { ReplicationTasksPageComponent } from './replication/replication-tasks-page/replication-tasks-page.component'; + import { DestinationPageComponent } from './replication/destination/destination-page.component'; import { ReplicationPageComponent } from './replication/replication-page.component'; @@ -99,6 +101,12 @@ const harborRoutes: Routes = [ canActivate: [SystemAdminGuard], canActivateChild: [SystemAdminGuard], }, + { + path: 'replications/:id/:tasks', + component: ReplicationTasksPageComponent, + canActivate: [SystemAdminGuard], + canActivateChild: [SystemAdminGuard], + }, { path: 'tags/:id/:repo', component: TagRepositoryComponent, diff --git a/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.html b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.html new file mode 100644 index 000000000..0f398b936 --- /dev/null +++ b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.scss b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.spec.ts b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.spec.ts new file mode 100644 index 000000000..1c44388f3 --- /dev/null +++ b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReplicationTasksPageComponent } from './replication-tasks-page.component'; + +describe('ReplicationTasksPageComponent', () => { + let component: ReplicationTasksPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReplicationTasksPageComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReplicationTasksPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.ts b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.ts new file mode 100644 index 000000000..83fe8b6d5 --- /dev/null +++ b/src/portal/src/app/replication/replication-tasks-page/replication-tasks-page.component.ts @@ -0,0 +1,18 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +@Component({ + selector: 'app-replication-tasks-page', + templateUrl: './replication-tasks-page.component.html', + styleUrls: ['./replication-tasks-page.component.scss'] +}) +export class ReplicationTasksPageComponent implements OnInit { + executionId: string; + constructor( + private route: ActivatedRoute, + ) { } + + ngOnInit(): void { + this.executionId = this.route.snapshot.params["id"]; + } + +} diff --git a/src/portal/src/app/replication/replication.module.ts b/src/portal/src/app/replication/replication.module.ts index 841110a71..76531a553 100644 --- a/src/portal/src/app/replication/replication.module.ts +++ b/src/portal/src/app/replication/replication.module.ts @@ -18,6 +18,7 @@ import { ReplicationManagementComponent } from './replication-management/replica import { ReplicationPageComponent } from './replication-page.component'; import { TotalReplicationPageComponent } from './total-replication/total-replication-page.component'; import { DestinationPageComponent } from './destination/destination-page.component'; +import { ReplicationTasksPageComponent } from './replication-tasks-page/replication-tasks-page.component'; import { SharedModule } from '../shared/shared.module'; import {ReactiveFormsModule} from "@angular/forms"; @@ -32,11 +33,13 @@ import {ReactiveFormsModule} from "@angular/forms"; ReplicationPageComponent, ReplicationManagementComponent, TotalReplicationPageComponent, + ReplicationTasksPageComponent, DestinationPageComponent, ], exports: [ ReplicationPageComponent, DestinationPageComponent, + ReplicationTasksPageComponent, TotalReplicationPageComponent, ] }) diff --git a/src/portal/src/app/shared/shared.module.ts b/src/portal/src/app/shared/shared.module.ts index 08e7310ab..36ee422e1 100644 --- a/src/portal/src/app/shared/shared.module.ts +++ b/src/portal/src/app/shared/shared.module.ts @@ -62,10 +62,9 @@ const uiLibConfig: IServiceConfig = { systemInfoEndpoint: "/api/systeminfo", repositoryBaseEndpoint: "/api/repositories", logBaseEndpoint: "/api/logs", - targetBaseEndpoint: "/api/targets", - replicationBaseEndpoint: "/api/replications", - replicationRuleEndpoint: "/api/policies/replication", - replicationJobEndpoint: "/api/jobs/replication", + targetBaseEndpoint: "/api/registries", + replicationBaseEndpoint: "/api/replication/executions", + replicationRuleEndpoint: "/api/replication/policies", vulnerabilityScanningBaseEndpoint: "/api/repositories", projectPolicyEndpoint: "/api/projects/configs", projectBaseEndpoint: "/api/projects", diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 36f8e1d65..4eafd0c3f 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -17,6 +17,7 @@ "TITLE": "Sign Up" }, "BUTTON": { + "STOP": "STOP", "CANCEL": "CANCEL", "OK": "OK", "DELETE": "DELETE", @@ -330,6 +331,18 @@ "OF": "of" }, "REPLICATION": { + "STOP_TITLE": "Confirm Stop Executions", + "STOP_SUMMARY": "Do you want to stop the executions {{param}}?", + "TASK_ID":"Task ID", + "RECOURCE_TYPE": "Recource Type", + "RECOURCE": "Recource", + "DESTINATION": "Destination", + "POLICY": "Policy", + "DURATION": "Duration", + "SUCCESS_RATE": "Success Rate", + "SUCCESS": "Success", + "FAILTURE": "Failture", + "IN_PROGRESS": "In Progress", "REPLICATION_RULE": "Replication Rule", "NEW_REPLICATION_RULE": "New Replication Rule", "ENDPOINTS": "Endpoints", @@ -353,13 +366,16 @@ "TESTING_CONNECTION": "Testing Connection...", "TEST_CONNECTION_SUCCESS": "Connection tested successfully.", "TEST_CONNECTION_FAILURE": "Failed to ping endpoint.", + "ID":"ID", "NAME": "Name", - "PROJECT": "Project", "NAME_IS_REQUIRED": "Name is required.", "DESCRIPTION": "Description", "ENABLE": "Enable", "DISABLE": "Disable", - "DESTINATION_NAME": "Endpoint Name", + "REPLICATION_MODE": "Replication Mode", + "SRC_NAMESPACE": "Source registry:Namespace", + "DESTINATION_NAMESPACE": "Destination registry:Namespace", + "LAST_REPLICATION":"Last Replication", "DESTINATION_NAME_IS_REQUIRED": "Endpoint name is required.", "NEW_DESTINATION": "New Endpoint", "DESTINATION_URL": "Endpoint URL", @@ -371,8 +387,9 @@ "DISABLED": "Disabled", "LAST_START_TIME": "Last Start Time", "ACTIVATION": "Activation", - "REPLICATION_JOBS": "Replication Jobs", - "STOPJOB": "Stop Jobs", + "REPLICATION_EXECUTION": "Execution", + "REPLICATION_EXECUTIONS": "Executions", + "STOPJOB": "Stop", "ALL": "All", "PENDING": "Pending", "RUNNING": "Running", @@ -384,9 +401,10 @@ "SIMPLE": "Simple", "ADVANCED": "Advanced", "STATUS": "Status", - "OPERATION": "Operation", + "REPLICATION_TRIGGER": "Trigger", "CREATION_TIME": "Start Time", "UPDATE_TIME": "Update Time", + "END_TIME": "End Time", "LOGS": "Logs", "OF": "of", "ITEMS": "items", @@ -840,6 +858,7 @@ "ALL": "All", "RUNNING": "Running", "FAILED": "Failed", + "STOP_EXECUTIONS": "Stop Execution", "DELETE_PROJECT": "Delete project", "DELETE_REPO": "Delete repository", "DELETE_TAG": "Delete tag", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 776694506..a789b0b43 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -17,6 +17,7 @@ "TITLE": "Registrarse" }, "BUTTON": { + "STOP": "STOP", "CANCEL": "CANCELAR", "OK": "OK", "DELETE": "ELIMINAR", @@ -329,6 +330,20 @@ "OF": "of" }, "REPLICATION": { + "STOP_TITLE": "Confirme Stop Executions", + "STOP_SUMMARY": "De que desea detener las ejecuciones {{param}}?", + "TASK_ID":"Task ID", + "RECOURCE_TYPE": "Recource Type", + "RECOURCE": "Recource", + "DESTINATION": "Destination", + "POLICY": "Policy", + "DURATION": "Duration", + "SUCCESS_RATE": "Success Rate", + "SUCCESS": "Success", + "FAILTURE": "Failture", + "IN_PROGRESS": "In Progress", + "STOP_EXECUTIONS": "Stop Execution", + "ID":"ID", "REPLICATION_RULE": "Reglas de Replicación", "NEW_REPLICATION_RULE": "Nueva Regla de Replicación", "ENDPOINTS": "Endpoints", @@ -358,7 +373,10 @@ "DESCRIPTION": "Descripción", "ENABLE": "Activar", "DISABLE": "Desactivar", - "DESTINATION_NAME": "Nombre del Endpoint", + "REPLICATION_MODE": "Replication Mode", + "SRC_NAMESPACE": "Source registry:Namespace", + "DESTINATION_NAMESPACE": "Destination registry:Namespace", + "LAST_REPLICATION":"Last Replication", "DESTINATION_NAME_IS_REQUIRED": "El nombre del endpoint es obligatorio.", "NEW_DESTINATION": "Nuevo Endpoint", "DESTINATION_URL": "URL del Endpoint", @@ -370,8 +388,10 @@ "DISABLED": "Desactivado", "LAST_START_TIME": "Última Fecha de Inicio", "ACTIVATION": "Activación", - "REPLICATION_JOBS": "Trabajos de Replicación", - "STOPJOB": "Stop Jobs", + "REPLICATION_EXECUTION": "Trabajo de Replicación", + "REPLICATION_EXECUTIONS": "Trabajos de Replicación", + "END_TIME": "End Time", + "STOPJOB": "Stop", "ALL": "Todos", "PENDING": "Pendiente", "RUNNING": "Ejecutando", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index 2a4b19715..bcc7ecf0d 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -17,6 +17,7 @@ "TITLE": "S'inscrire" }, "BUTTON": { + "STOP": "STOP", "CANCEL": "ANNULER", "OK": "OK", "DELETE": "SUPPRIMER", @@ -314,6 +315,20 @@ "OF": "de" }, "REPLICATION": { + "STOP_TITLE": "Confirmer arrêter les exécutions", + "STOP_SUMMARY": "Voulez-vous arrêter les exécutions {{param}}?", + "TASK_ID":"Task ID", + "RECOURCE_TYPE": "Recource Type", + "RECOURCE": "Recource", + "DESTINATION": "Destination", + "POLICY": "Policy", + "DURATION": "Duration", + "SUCCESS_RATE": "Success Rate", + "SUCCESS": "Success", + "FAILTURE": "Failture", + "IN_PROGRESS": "In Progress", + "STOP_EXECUTIONS": "Stop Execution", + "ID":"ID", "REPLICATION_RULE": "Règle de Réplication", "NEW_REPLICATION_RULE": "Nouvelle Règle de Réplication", "ENDPOINTS": "Points finaux", @@ -340,7 +355,10 @@ "DESCRIPTION": "Description", "ENABLE": "Activer", "DISABLE": "Désactiver", - "DESTINATION_NAME": "Nom du Point Final", + "REPLICATION_MODE": "Replication Mode", + "SRC_NAMESPACE": "Source registry:Namespace", + "DESTINATION_NAMESPACE": "Destination registry:Namespace", + "LAST_REPLICATION":"Last Replication", "DESTINATION_NAME_IS_REQUIRED": "Le nom du Point Final est obligatoire.", "NEW_DESTINATION": "Nouveau Point Final", "DESTINATION_URL": "URL du Point Final", @@ -352,8 +370,10 @@ "DISABLED": "Désactivé", "LAST_START_TIME": "Dernière heure de démarrage", "ACTIVATION": "Activation", - "REPLICATION_JOBS": "Travaux de Réplication", + "REPLICATION_EXECUTION": "Travaux de Réplication", + "REPLICATION_EXECUTIONS": "Travaux de Réplication", "ALL": "Tous", + "END_TIME": "End Time", "PENDING": "En attente", "RUNNING": "En fonctionnement", "ERROR": "Erreur", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 8ad4c5c83..f1fc885f2 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -17,6 +17,7 @@ "TITLE": "Registrar-se" }, "BUTTON": { + "STOP": "STOP", "CANCEL": "CANCELAR", "OK": "OK", "DELETE": "DELETAR", @@ -328,6 +329,20 @@ "OF": "de" }, "REPLICATION": { + "STOP_TITLE": "Confirme as execuções de parada", + "STOP_SUMMARY": "Você quer parar as execuções? {{param}}?", + "TASK_ID":"Task ID", + "RECOURCE_TYPE": "Recource Type", + "RECOURCE": "Recource", + "DESTINATION": "Destination", + "POLICY": "Policy", + "DURATION": "Duration", + "SUCCESS_RATE": "Success Rate", + "SUCCESS": "Success", + "FAILTURE": "Failture", + "IN_PROGRESS": "In Progress", + "STOP_EXECUTIONS": "Stop Execution", + "ID":"ID", "REPLICATION_RULE": "Regra de replicação", "NEW_REPLICATION_RULE": "Nova regra de replicação", "ENDPOINTS": "Endpoints", @@ -357,7 +372,10 @@ "DESCRIPTION": "Descrição", "ENABLE": "Habilitar", "DISABLE": "Desabilitar", - "DESTINATION_NAME": "Nome do Endpoint", + "REPLICATION_MODE": "Replication Mode", + "SRC_NAMESPACE": "Source registry:Namespace", + "DESTINATION_NAMESPACE": "Destination registry:Namespace", + "LAST_REPLICATION":"Last Replication", "DESTINATION_NAME_IS_REQUIRED": "Nome do Endpoint é obrigatório.", "NEW_DESTINATION": "Novo Endpoint", "DESTINATION_URL": "URL do Endpoint", @@ -369,8 +387,10 @@ "DISABLED": "Desabilitado", "LAST_START_TIME": "Ultima hora de início", "ACTIVATION": "Ativação", - "REPLICATION_JOBS": "Tarefas de Replicação", - "STOPJOB": "Parar tarefas", + "REPLICATION_EXECUTION": "ExecTarefa de Replicaçãoution", + "REPLICATION_EXECUTIONS": "Tarefas de Replicação", + "STOPJOB": "Parar", + "END_TIME": "End Time", "ALL": "Todas", "PENDING": "Pendentes", "RUNNING": "Executando", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index ee829b758..18a784fc0 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -17,6 +17,7 @@ "TITLE": "注册" }, "BUTTON": { + "STOP": "停止", "CANCEL": "取消", "OK": "确定", "DELETE": "删除", @@ -329,6 +330,20 @@ "OF": "共计" }, "REPLICATION": { + "STOP_TITLE": "确认停止任务", + "STOP_SUMMARY": "确认停止任务{{param}}?", + "TASK_ID":"任务ID", + "RECOURCE_TYPE": "源类型", + "RECOURCE": "源", + "DESTINATION": "目标", + "POLICY": "政策", + "DURATION": "到期时间", + "SUCCESS_RATE": "成功百分比", + "SUCCESS": "成功", + "FAILTURE": "失败", + "IN_PROGRESS": "进行中", + "STOP_EXECUTIONS": "停止任务", + "ID":"ID", "REPLICATION_RULE": "复制规则", "NEW_REPLICATION_RULE": "新建规则", "ENDPOINTS": "目标", @@ -358,7 +373,10 @@ "DESCRIPTION": "描述", "ENABLE": "启用", "DISABLE": "停用", - "DESTINATION_NAME": "目标名", + "REPLICATION_MODE": "复制模式", + "SRC_NAMESPACE": "源仓库:命名空间", + "DESTINATION_NAMESPACE": "目标仓库:命名空间", + "LAST_REPLICATION":"最后一次复制", "DESTINATION_NAME_IS_REQUIRED": "目标名称为必填项。", "NEW_DESTINATION": "创建目标", "DESTINATION_URL": "目标URL", @@ -370,7 +388,8 @@ "DISABLED": "停用", "LAST_START_TIME": "上次起始时间", "ACTIVATION": "活动状态", - "REPLICATION_JOBS": "复制任务", + "REPLICATION_EXECUTION": "复制任务", + "REPLICATION_EXECUTIONS": "复制任务", "STOPJOB": "停止任务", "ALL": "全部", "PENDING": "挂起", @@ -386,6 +405,7 @@ "OPERATION": "操作", "CREATION_TIME": "创建时间", "UPDATE_TIME": "更新时间", + "END_TIME": "结束时间", "LOGS": "日志", "OF": "共计", "ITEMS": "条记录",