mirror of
https://github.com/goharbor/harbor.git
synced 2024-09-12 14:08:23 +02:00
Merge pull request #7172 from pureshine/registry-management
List replication rules and executions page
This commit is contained in:
commit
9bff73a36e
@ -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".
|
||||
|
||||
|
@ -26,5 +26,9 @@
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="5">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.STOP' | translate}}</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</clr-modal>
|
@ -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)
|
||||
);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -7,28 +7,32 @@
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasExecuteReplicationPermission" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon> {{'REPLICATION.REPLICATE' | translate}}</button>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'name'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column *ngIf="!projectScope">{{'REPLICATION.PROJECT' | translate}}</clr-dg-column>
|
||||
<clr-dg-column class="min-width">{{'REPLICATION.SRC_NAMESPACE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.REPLICATION_MODE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column class="min-width">{{'REPLICATION.DESTINATION_NAMESPACE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.LAST_REPLICATION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.REPLICATION_TRIGGER' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column >{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.TRIGGER_MODE' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||
<clr-dg-cell>{{p.name}}</clr-dg-cell>
|
||||
<clr-dg-cell class="min-width">
|
||||
{{p.src_registry_id>0? srcRegistry : 'current'}} : {{p.src_namespaces?.length>0 ? p.src_namespaces[0]: ''}}
|
||||
<clr-tooltip>
|
||||
<clr-icon *ngIf="p.src_namespaces && p.src_namespaces.length > 1" clrTooltipTrigger shape="ellipsis-horizontal" size="18"></clr-icon>
|
||||
<clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen>
|
||||
<span>{{p.src_namespaces}}</span>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<div [ngSwitch]="hasDeletedLabel(p)">
|
||||
<clr-tooltip *ngSwitchCase="'disabled'" class="tooltip-lg">
|
||||
<clr-icon clrTooltipTrigger shape="exclamation-triangle" class="is-warning text-alignment" size="22"></clr-icon>Disabled
|
||||
<clr-tooltip-content clrPosition="top-right" clrSize="xs" *clrIfOpen>
|
||||
<span>{{'REPLICATION.RULE_DISABLED' | translate}}</span>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
<div *ngSwitchCase="'enabled'" ><clr-icon shape="success-standard" class="is-success text-alignment" size="18"></clr-icon> Enabled</div>
|
||||
</div>
|
||||
{{p.src_registry_id>0? 'pull-based' : 'push-based'}}
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell *ngIf="!projectScope">
|
||||
<a href="javascript:void(0)" (click)="$event.stopPropagation(); redirectTo(p)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>
|
||||
<clr-dg-cell class="min-width">
|
||||
{{p.dest_registry_id>0? srcRegistry : 'current'}} : {{p.dest_namespace? p.dest_namespace: ''}}
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.update_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.trigger ? p.trigger.type : ''}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
{{p.description ? trancatedDescription(p.description) : '-'}}
|
||||
<clr-tooltip>
|
||||
@ -38,8 +42,6 @@
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{p.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
||||
|
@ -4,4 +4,7 @@
|
||||
|
||||
.text-alignment {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
.min-width {
|
||||
width: 224px;
|
||||
}
|
||||
|
@ -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<ReplicationJob>(this.replicationService.getJobs(id))
|
||||
return toPromise<ReplicationJob>(this.replicationService.getExecutions(id))
|
||||
.then(response => {
|
||||
ruleData = response.data;
|
||||
if (ruleData.length) {
|
||||
|
@ -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<any>[] = [
|
||||
ReplicationComponent
|
||||
ReplicationComponent,
|
||||
ReplicationTasksComponent
|
||||
];
|
||||
|
@ -0,0 +1,117 @@
|
||||
<div class="replication-tasks">
|
||||
<section class="overview-section">
|
||||
<div class="title-wrapper clr-row">
|
||||
<div class="clr-col-lg-9">
|
||||
<div>
|
||||
<a (click)="onBack()" class="onback">< {{'PROJECT_DETAIL.REPLICATION'| translate}}</a>
|
||||
</div>
|
||||
<div class="title-block clr-row">
|
||||
<div class="clr-col-2">
|
||||
<h2 class="custom-h2">{{'REPLICATION.REPLICATION_EXECUTION'| translate}}</h2>
|
||||
<h4>{{executionId}}</h4>
|
||||
</div>
|
||||
<div class="clr-col-3">
|
||||
<span class="label label-info" *ngIf="executions === 'InProgress'">In Progress</span>
|
||||
<span class="label label-success" *ngIf="executions === 'success'">Success</span>
|
||||
<span class="label label-danger" *ngIf="executions=== 'failture'">Failture</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-col-3">
|
||||
<button class="btn btn-outline" (click)="stopJob()" [disabled]="stopOnGoing">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="execution-block row">
|
||||
<div class="clr-col-sm-12 clr-col-md-6">
|
||||
<div class="flex-block">
|
||||
<section class="execution-detail-label">
|
||||
<section class="detail-row">
|
||||
<span class="label label-purple detail-span">{{'REPLICATION.SUCCESS'| translate}}</span>
|
||||
<div class="execution-details">{{'1'}}</div>
|
||||
</section>
|
||||
<section class="detail-row">
|
||||
<span class="label label-red detail-span">{{'REPLICATION.FAILTURE'| translate}}</span>
|
||||
<div class="execution-details">{{'2'}}</div>
|
||||
</section>
|
||||
<section class="detail-row">
|
||||
<span class="label label-light-blue detail-span">{{'REPLICATION.IN_PROGRESS'| translate}}</span>
|
||||
<div class="execution-details">{{'3'}}</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clr-col-sm-12 clr-col-md-6">
|
||||
<div class="executions-detail">
|
||||
<div class="policy">
|
||||
<label>{{'REPLICATION.POLICY' | translate}} :</label>
|
||||
<span>{{'My 1st policy'}}</span>
|
||||
</div>
|
||||
<div class="trigger">
|
||||
<label for="">{{'REPLICATION.TRIGGER_MODE' | translate}} :</label>
|
||||
<span>{{'Schedule'}}</span>
|
||||
</div>
|
||||
<div class="start-time">
|
||||
<label for="">{{'REPLICATION.CREATION_TIME' | translate}} :</label>
|
||||
<span>{{'3/14/19, 2:26 PM'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="tasks-detail">
|
||||
<h3 class="modal-title">Tasks</h3>
|
||||
<div class="row flex-items-xs-between flex-items-xs-bottom">
|
||||
<div class="action-select">
|
||||
<div class="select filterTag" [hidden]="!isOpenFilterTag">
|
||||
<select>
|
||||
<option value="username">{{"AUDIT_LOG.USERNAME" | translate | lowercase}}</option>
|
||||
<option value="repository">{{"CONFIG.REPOSITORY" | translate | lowercase}}</option>
|
||||
<option value="tag">{{"REPOSITORY.TAG" | translate | lowercase}}</option>
|
||||
<option value="operation">{{"AUDIT_LOG.OPERATION" | translate | lowercase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<hbr-filter [withDivider]="true" (openFlag)="openFilter($event)" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}'></hbr-filter>
|
||||
<span class="refresh-btn">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
<span class="spinner spinner-inline" hidden></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<clr-datagrid [(clrDgSelected)]="selectedRow">
|
||||
<clr-dg-column>{{'REPLICATION.TASK_ID'| translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'resource_type'">{{'REPLICATION.RECOURCE_TYPE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'src_resource'">{{'REPLICATION.RECOURCE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'dst_resource'">{{'REPLICATION.DESTINATION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="startTimeComparator">{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="endTimeComparator">{{'REPLICATION.END_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *clrDgItems="let t of tasks">
|
||||
<clr-dg-cell>{{t.id}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.resource_type}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.src_resource}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.dst_resource}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.status}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.start_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{t.end_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<span *ngIf="t.status=='InProgress'; else elseBlock" class="label">{{'REPLICATION.NO_LOGS' | translate}}</span>
|
||||
<ng-template #elseBlock>
|
||||
<a target="_blank" [href]="viewLog(t.id)">
|
||||
<clr-icon shape="list"></clr-icon>
|
||||
</a>
|
||||
</ng-template>
|
||||
</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}}
|
||||
-
|
||||
{{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' |
|
||||
translate}} </span>
|
||||
{{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ReplicationJob> = new CustomComparator<
|
||||
ReplicationJob
|
||||
>("start_time", "date");
|
||||
endTimeComparator: Comparator<ReplicationJob> = 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<ReplicationJob>(
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 jobList" [hidden]='hiddenJobList'>
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="row flex-items-xs-between jobsRow">
|
||||
<h5 class="flex-items-xs-bottom option-left-down">{{'REPLICATION.REPLICATION_JOBS' | translate}}</h5>
|
||||
<h5 class="flex-items-xs-bottom option-left-down">{{'REPLICATION.REPLICATION_EXECUTIONS' | translate}}</h5>
|
||||
<div class="flex-items-xs-bottom option-right-down">
|
||||
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filterEvt)="doSearchJobs($event)"
|
||||
@ -46,34 +46,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)">
|
||||
<clr-datagrid [(clrDgSelected)]="selectedRow" [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)">
|
||||
<clr-dg-action-bar>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!(jobs && jobs.length>0) || isStopOnGoing"
|
||||
(click)="stopJobs()">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary" *ngIf="isSystemAdmin" [disabled]="!(jobs && jobs.length>0) || isStopOnGoing || !selectedRow.length"
|
||||
(click)="openStopExecutionsDialog(selectedRow)">{{'REPLICATION.STOPJOB' | translate}}</button>
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'id'">{{'REPLICATION.ID' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.REPLICATION_TRIGGER' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.UPDATE_TIME' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.DURATION' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'REPLICATION.SUCCESS_RATE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let j of jobs">
|
||||
<clr-dg-cell>{{j.repository}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.operation}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.update_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-row *ngFor="let j of jobs" [clrDgItem]="j">
|
||||
<clr-dg-cell>
|
||||
<span *ngIf="j.status=='pending'; else elseBlock" class="label">{{'REPLICATION.NO_LOGS' | translate}}</span>
|
||||
<ng-template #elseBlock>
|
||||
<a target="_blank" [href]="viewLog(j.id)">
|
||||
<clr-icon shape="list"></clr-icon>
|
||||
</a>
|
||||
</ng-template>
|
||||
<a href="javascript:void(0)" (click)="goToLink(j.id)">{{j.id}}</a>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.trigger}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.start_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{'3mins'}}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
{{'90%'}}
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{j.status}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="showPaginationIndex">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}</span>
|
||||
@ -86,4 +83,5 @@
|
||||
<hbr-create-edit-rule *ngIf="isSystemAdmin" [withAdmiral]="withAdmiral" [projectId]="projectId" [projectName]="projectName"
|
||||
(goToRegistry)="goRegistry()" (reload)="reloadRules($event)"></hbr-create-edit-rule>
|
||||
<confirmation-dialog #replicationConfirmDialog (confirmAction)="confirmReplication($event)"></confirmation-dialog>
|
||||
<confirmation-dialog #StopConfirmDialog (confirmAction)="confirmStop($event)"></confirmation-dialog>
|
||||
</div>
|
@ -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));
|
||||
|
||||
|
@ -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<ReplicationJob> = new CustomComparator<
|
||||
ReplicationJob
|
||||
>("creation_time", "date");
|
||||
>("start_time", "date");
|
||||
updateTimeComparator: Comparator<ReplicationJob> = 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<ReplicationJob>(
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -132,7 +132,7 @@ export class EndpointDefaultService extends EndpointService {
|
||||
super();
|
||||
this._endpointUrl = config.targetBaseEndpoint
|
||||
? config.targetBaseEndpoint
|
||||
: "/api/targets";
|
||||
: "/api/registries";
|
||||
}
|
||||
|
||||
public getEndpoints(
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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");
|
||||
}));
|
||||
});
|
||||
|
@ -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";
|
||||
|
@ -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");
|
||||
});
|
||||
});
|
||||
|
@ -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<ReplicationRule> | Promise<ReplicationRule> | ReplicationRule;
|
||||
|
||||
|
||||
/**
|
||||
* Get the specified replication task.
|
||||
*
|
||||
* @abstract
|
||||
* returns {(Observable<ReplicationRule>)}
|
||||
*
|
||||
* @memberOf ReplicationService
|
||||
*/
|
||||
abstract getReplicationTasks(
|
||||
executionId: number | string
|
||||
): Observable<ReplicationTasks>;
|
||||
/**
|
||||
* Create new replication rule.
|
||||
*
|
||||
@ -146,7 +159,7 @@ export abstract class ReplicationService {
|
||||
*
|
||||
* @memberOf ReplicationService
|
||||
*/
|
||||
abstract getJobs(
|
||||
abstract getExecutions(
|
||||
ruleId: number | string,
|
||||
queryParams?: RequestQueryParams
|
||||
): Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob;
|
||||
@ -165,7 +178,7 @@ export abstract class ReplicationService {
|
||||
|
||||
abstract stopJobs(
|
||||
jobId: number | string
|
||||
): Observable<string> | Promise<string> | string;
|
||||
): Observable<string>;
|
||||
|
||||
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<ReplicationTasks> {
|
||||
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<any> | Promise<any> | 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<ReplicationJob> | Promise<ReplicationJob> | 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<any> | Promise<any> | 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)));
|
||||
}
|
||||
}
|
||||
|
@ -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 = [
|
||||
|
@ -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,
|
||||
|
@ -0,0 +1 @@
|
||||
<replication-tasks [executionId]="executionId"></replication-tasks>
|
@ -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<ReplicationTasksPageComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ReplicationTasksPageComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ReplicationTasksPageComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -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"];
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
]
|
||||
})
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "条记录",
|
||||
|
Loading…
Reference in New Issue
Block a user