Merge pull request #4542 from pengpengshui/tagdetail

Add repo read only
This commit is contained in:
pengpengshui 2018-04-02 13:41:46 +08:00 committed by GitHub
commit 6a1af52949
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 236 additions and 69 deletions

View File

@ -20,4 +20,5 @@ export const CREATE_EDIT_LABEL_STYLE: string = `
height:22px;
min-width: 26px;}
.dropdown-item{border:0px; color: white; font-size:12px;}
.dropdown .dropdown-toggle.btn{padding:0 !important;}
`;

View File

@ -19,7 +19,7 @@ h4{
}
.colorRed{color: red;}
.colorRed a{text-decoration: underline;color: #007CBB;}
.alertLabel{display:block; margin-top:0; line-height:1em; font-size:12px;}
.alertLabel{display:block; margin-top:2px; line-height:1em; font-size:12px;}
.inputWidth{width: 270px;}
.endpointSelect{ width: 270px; margin-right: 20px;}

View File

@ -11,7 +11,7 @@ export const LABEL_PIEICE_TEMPLATE: string = `
`;
export const LABEL_PIEICE_STYLES: string = `
.label{border: none; color:#222;padding-top:2px;}
.label{border: none; color:#222;padding-top:2px; max-width:140px;}
.label clr-icon{ margin-right: 3px; display:block;}
.btn-group .dropdown-menu clr-icon{display:block;}
`;

View File

@ -112,8 +112,8 @@ export class LabelComponent implements OnInit {
this.batchDelectionInfos.push(initBatchMessage);
});
let deletionMessage = new ConfirmationMessage(
'REPLICATION.DELETION_TITLE_TARGET',
'REPLICATION.DELETION_SUMMARY_TARGET',
'LABEL.DELETION_TITLE_TARGET',
'LABEL.DELETION_SUMMARY_TARGET',
targetNames.join(', ') || '',
targets,
ConfirmationTargets.TARGET,

View File

@ -176,6 +176,13 @@ export class RepositoryListviewComponent implements OnChanges, OnInit {
});
return;
}
if (error.status === 503) {
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
});
return;
}
this.translateService.get('BATCH.DELETED_FAILURE').subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});

View File

@ -30,7 +30,7 @@ export const TAG_DETAIL_HTML: string = `
</div>
</div>
</div>
<div>
<div *ngIf="withClair">
<div class="vulnerability">
<hbr-vulnerability-bar [repoName]="repositoryId" [tagId]="tagDetails.name" [summary]="tagDetails.scan_overview"></hbr-vulnerability-bar>
</div>

View File

@ -148,18 +148,4 @@ describe('TagDetailComponent (inline template)', () => {
});
}));
it('should display vulnerability details', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
let el: HTMLElement = fixture.nativeElement.querySelector('.second-column');
expect(el).toBeTruthy();
let el2: HTMLElement = el.querySelector('div');
expect(el2).toBeTruthy();
expect(el2.textContent.trim()).toEqual("13 VULNERABILITY.SEVERITY.HIGHTAG.LEVEL_VULNERABILITIES");
});
}));
});

View File

@ -25,6 +25,7 @@ export class TagDetailComponent implements OnInit {
@Input() tagId: string;
@Input() repositoryId: string;
@Input() withAdmiral: boolean;
@Input() withClair: boolean;
tagDetails: Tag = {
name: "--",
size: "--",

View File

@ -67,10 +67,6 @@ export const TAG_STYLE = `
:host >>> .signpost-content-header{display:none;}
.filterLabelPiece{position: absolute; bottom :0px;z-index:1;}
.dropdown .dropdown-toggle.btn {
padding-right: 1rem;
border-left-width: 0;
border-right-width: 0;
border-radius: 0;
margin-top: -2px;
margin: .25rem .5rem .25rem 0;
}
`;

View File

@ -44,23 +44,23 @@ export const TAG_TEMPLATE = `
<clr-dg-action-bar>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(canScanNow(selectedRow) && selectedRow.length==1)" (click)="scanNow(selectedRow)"><clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length==1)" (click)="showDigestId(selectedRow)" ><clr-icon shape="copy" size="16"></clr-icon>&nbsp;{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
<clr-dropdown *ngIf="!withAdmiral" class="btn btn-sm btn-secondary">
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1) || isGuest" (click)="addLabels(selectedRow)" >{{'REPOSITORY.ADD_LABELS' | translate}}</button>
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
<div style='display:grid'>
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
<div class="form-group"><input type="text" placeholder="Filter labels" #stickLabelNamePiece (keyup)="handleStickInputFilter(stickLabelNamePiece.value)"></div>
<div [hidden]='imageStickLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="selectLabel(label); label.iconsShow = true">
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
<div class='labelDiv'><hbr-label-piece [label]="label.label"></hbr-label-piece></div>
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); unSelectLabel(label); label.iconsShow = false"></clr-icon>
</button>
</div>
</div>
</clr-dropdown-menu>
</clr-dropdown>
<clr-dropdown *ngIf="!withAdmiral">
<button type="button" class="btn btn-sm btn-secondary" clrDropdownTrigger [disabled]="!(selectedRow.length==1) || isGuest" (click)="addLabels(selectedRow)" ><clr-icon shape="plus" size="16"></clr-icon>{{'REPOSITORY.ADD_LABELS' | translate}}</button>
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
<div style='display:grid'>
<label class="dropdown-header">{{'REPOSITORY.ADD_TO_IMAGE' | translate}}</label>
<div class="form-group"><input type="text" placeholder="Filter labels" #stickLabelNamePiece (keyup)="handleStickInputFilter(stickLabelNamePiece.value)"></div>
<div [hidden]='imageStickLabels.length'>{{'LABEL.NO_LABELS' | translate }}</div>
<div [hidden]='!imageStickLabels.length' style='max-height:300px;overflow-y: auto;'>
<button type="button" class="dropdown-item" *ngFor='let label of imageStickLabels' (click)="selectLabel(label); label.iconsShow = true">
<clr-icon shape="check" class='pull-left' [hidden]='!label.iconsShow'></clr-icon>
<div class='labelDiv'><hbr-label-piece [label]="label.label"></hbr-label-piece></div>
<clr-icon shape="times-circle" class='pull-right' [hidden]='!label.iconsShow' (click)="$event.stopPropagation(); unSelectLabel(label); label.iconsShow = false"></clr-icon>
</button>
</div>
</div>
</clr-dropdown-menu>
</clr-dropdown>
<button type="button" class="btn btn-sm btn-secondary" *ngIf="hasProjectAdminRole" (click)="deleteTags(selectedRow)" [disabled]="!selectedRow.length"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'REPOSITORY.DELETE' | translate}}</button>
</clr-dg-action-bar>
<clr-dg-column style="width: 120px;" [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
@ -74,9 +74,8 @@ export const TAG_TEMPLATE = `
<clr-dg-column *ngIf="!withAdmiral" style="width: 140px;" [clrDgField]="'labels'">{{'REPOSITORY.LABELS' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'TAG.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let t of tags" [clrDgItem]='t'>
<clr-dg-cell class="truncated" style="width: 120px;" [ngSwitch]="withClair">
<a *ngSwitchCase="true" href="javascript:void(0)" (click)="onTagClick(t)" title="{{t.name}}">{{t.name}}</a>
<span *ngSwitchDefault>{{t.name}}</span>
<clr-dg-cell class="truncated" style="width: 120px;">
<a href="javascript:void(0)" (click)="onTagClick(t)" title="{{t.name}}">{{t.name}}</a>
</clr-dg-cell>
<clr-dg-cell style="width: 90px;">{{sizeTransform(t.size)}}</clr-dg-cell>
<clr-dg-cell style="min-width: 100px; max-width:220px;" class="truncated" title="docker pull {{registryUrl}}/{{repoName}}:{{t.name}}">

View File

@ -217,7 +217,13 @@ export class TagComponent implements OnInit, AfterViewInit {
st.page.size = this.pageSize;
st.page.from = 0;
st.page.to = this.pageSize - 1;
st.filters = [{property: "name", value: this.lastFilteredTagName}];
let selectedLab = this.imageFilterLabels.find(label => label.iconsShow === true);
if (selectedLab) {
st.filters = [{property: 'name', value: this.lastFilteredTagName}, {property: 'labels.name', value: selectedLab.label}];
}else {
st.filters = [{property: 'name', value: this.lastFilteredTagName}];
}
this.clrLoad(st);
}
@ -514,6 +520,13 @@ export class TagComponent implements OnInit, AfterViewInit {
findedList = BathInfoChanges(findedList, res);
});
}).catch(error => {
if (error.status === 503) {
Observable.forkJoin(this.translateService.get('BATCH.DELETED_FAILURE'),
this.translateService.get('REPOSITORY.TAGS_NO_DELETE')).subscribe(res => {
findedList = BathInfoChanges(findedList, res[0], false, true, res[1]);
});
return;
}
this.translateService.get("BATCH.DELETED_FAILURE").subscribe(res => {
findedList = BathInfoChanges(findedList, res, false, true);
});

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.10.17",
"clarity-ui": "^0.10.27",
"core-js": "^2.4.1",
"harbor-ui": "0.6.61",
"harbor-ui": "0.6.62",
"intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0",

View File

@ -27,6 +27,7 @@ export class AppConfig {
clair_vulnerability_status?: ClairDBStatus;
next_scan_all: number;
registry_storage_provider_name: string;
read_only: boolean;
constructor() {
// Set default value
@ -46,5 +47,6 @@ export class AppConfig {
};
this.next_scan_all = 0;
this.registry_storage_provider_name = "";
this.read_only = false;
}
}

View File

@ -5,6 +5,9 @@
.container-override {
position: relative !important;
}
.content-container{
position: relative;
}
.start-content-padding {
padding: 0px !important;

View File

@ -69,12 +69,16 @@ export class NavigatorComponent implements OnInit {
this.translate.onLangChange.subscribe((langChange: {lang: string}) => {
this.selectedLang = langChange.lang;
//Keep in cookie for next use
let opt:CookieOptions = {path: '/', expires: new Date(Date.now() + 3600*1000*24*31)};
let opt: CookieOptions = {path: '/', expires: new Date(Date.now() + 3600*1000*24*31)};
this.cookie.put("harbor-lang", langChange.lang, opt);
});
if (this.appConfigService.isIntegrationMode()) {
this.appTitle = 'APP_TITLE.VIC';
}
if (this.appConfigService.getConfig().read_only) {
this.msgHandler.handleReadOnly();
}
}
public get isSessionValid(): boolean {

View File

@ -18,6 +18,9 @@
<li role="presentation" class="nav-item" *ngIf="withClair">
<button id="config-vulnerability" class="btn btn-link nav-link" aria-controls="vulnerability" [class.active]='isCurrentTabLink("config-vulnerability")' type="button" (click)='tabLinkClick("config-vulnerability")'>{{'CONFIG.VULNERABILITY' | translate}}</button>
</li>
<li role="presentation" class="nav-item">
<button id="config-repo" class="btn btn-link nav-link" aria-controls="repoReadOnly" [class.active]='isCurrentTabLink("config-repo")' type="button" (click)='tabLinkClick("config-repo")'>{{'CONFIG.REPO_READ_ONLY' | translate}}</button>
</li>
</ul>
<section id="authentication" role="tabpanel" aria-labelledby="config-auth" [hidden]='!isCurrentTabContent("authentication")'>
<config-auth [allConfig]="allConfig"></config-auth>
@ -34,6 +37,9 @@
<section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config>
</section>
<section id="repoReadOnly" role="tabpanel" aria-labelledby="config-repo" [hidden]='!isCurrentTabContent("repoReadOnly")'>
<repo-read-only [(repoConfig)]="allConfig"></repo-read-only>
</section>
<div>
<button type="button" class="btn btn-primary" (click)="save()" [hidden]="hideBtn" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="hideBtn" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.CANCEL' | translate}}</button>

View File

@ -31,6 +31,7 @@ import {
SystemSettingsComponent,
VulnerabilityConfigComponent,
} from 'harbor-ui';
import {RepoReadOnlyComponent} from "./repo/repo-read-only.component";
const fakePass = 'aWpLOSYkIzJTTU4wMDkx';
const TabLinkContentMap = {
@ -39,7 +40,8 @@ const TabLinkContentMap = {
'config-email': 'email',
'config-system': 'system_settings',
'config-vulnerability': 'vulnerability',
'config-label': 'system_label'
'config-label': 'system_label',
'config-repo': 'repoReadOnly'
};
@Component({
@ -60,6 +62,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
@ViewChild(VulnerabilityConfigComponent) vulnerabilityConfig: VulnerabilityConfigComponent;
@ViewChild(ConfigurationEmailComponent) mailConfig: ConfigurationEmailComponent;
@ViewChild(ConfigurationAuthComponent) authConfig: ConfigurationAuthComponent;
@ViewChild(RepoReadOnlyComponent) repoConfig: RepoReadOnlyComponent;
constructor(
private msgHandler: MessageHandlerService,
@ -125,6 +128,9 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
case 'config-vulnerability':
properties = ['scan_all_policy'];
break;
case 'config-repo':
properties = ['read_only'];
break;
default:
return null;
}
@ -262,6 +268,14 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
// HERE we choose force way
this.retrieveConfig();
if (changes['read_only']) {
this.msgHandler.handleReadOnly();
}
if (changes['read_only'].toString() === "false") {
this.msgHandler.clear();
}
// Reload bootstrap option
this.appConfigService.load().catch(error => console.error('Failed to reload bootstrap option with error: ', error));
@ -273,7 +287,7 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
});
} else {
// Inprop situation, should not come here
console.error('Save obort becasue nothing changed');
console.error('Save abort because nothing changed');
}
}

View File

@ -19,6 +19,7 @@ import { ConfigurationComponent } from './config.component';
import { ConfigurationService } from './config.service';
import { ConfigurationAuthComponent } from './auth/config-auth.component';
import { ConfigurationEmailComponent } from './email/config-email.component';
import {RepoReadOnlyComponent} from "./repo/repo-read-only.component";
@NgModule({
imports: [
@ -28,7 +29,9 @@ import { ConfigurationEmailComponent } from './email/config-email.component';
declarations: [
ConfigurationComponent,
ConfigurationAuthComponent,
ConfigurationEmailComponent],
ConfigurationEmailComponent,
RepoReadOnlyComponent
],
exports: [ConfigurationComponent],
providers: [ConfigurationService]
})

View File

@ -0,0 +1,25 @@
import {Component, Input, ViewChild} from "@angular/core";
import {Configuration} from "harbor-ui";
import {NgForm} from "@angular/forms";
@Component({
selector: 'repo-read-only',
templateUrl: 'repo-read-only.html',
})
export class RepoReadOnlyComponent {
@Input('repoConfig') currentConfig: Configuration = new Configuration();
@ViewChild('repoConfigFrom') repoForm: NgForm;
constructor() { }
disabled(prop: any) {
return !(prop && prop.editable);
}
setInsecureReadOnlyValue($event: any) {
this.currentConfig.read_only.value = $event;
}
}

View File

@ -0,0 +1,13 @@
<form #repoConfigFrom="ngForm" class="form">
<section class="form-block">
<div class="form-group">
<label for="repoReadOnly">{{'CONFIG.REPO_READ_ONLY' | translate}}</label>
<clr-checkbox name="repoReadOnly" id="repoReadOnly" [clrChecked]="currentConfig.read_only.value" [clrDisabled]="disabled(currentConfig.read_only)" (clrCheckedChange)="setInsecureReadOnlyValue($event)">
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right" style="top:-7px;">
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
<span class="tooltip-content">{{'CONFIG.REPO_TOOLTIP' | translate}}</span>
</a>
</clr-checkbox>
</div>
</section>
</form>

View File

@ -1,7 +1,7 @@
.global-message-alert {
position: fixed;
top: 60px;
left: 0px;
position: absolute;
top: 0;
left: 0;
width: 100%;
z-index: 999;
}
@ -11,4 +11,5 @@
}
:host >>> .alert-icon-wrapper{
display: inline;
}
}
:host >>> .alert.alert-app-level.alert-warning{padding: 0;}

View File

@ -1,3 +1,3 @@
<div>
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [withAdmiral]="withAdmiral" [repositoryId]="repositoryId"></hbr-tag-detail>
<hbr-tag-detail (backEvt)="goBack($event)" [tagId]="tagId" [withClair]="withClair" [withAdmiral]="withAdmiral" [repositoryId]="repositoryId"></hbr-tag-detail>
</div>

View File

@ -41,6 +41,10 @@ export class TagDetailPageComponent implements OnInit {
return this.appConfigService.getConfig().with_admiral;
}
get withClair(): boolean {
return this.appConfigService.getConfig().with_clair;
}
goBack(tag: string): void {
this.router.navigate(["harbor", "projects", this.projectId, "repositories", tag]);
}

View File

@ -53,6 +53,10 @@ export class MessageHandlerService implements ErrorHandler{
}
}
public handleReadOnly(): void {
this.msgService.announceAppLevelMessage(503, 'REPO_READ_ONLY', AlertType.WARNING);
}
public showError(message: string, params: any): void {
if (!params) {
params = {};

View File

@ -48,6 +48,10 @@ export class AuthCheckGuard implements CanActivate, CanActivateChild {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> | boolean {
//When routing change, clear
this.msgHandler.clear();
if (this.appConfigService.getConfig().read_only.toString() === 'true') {
this.msgHandler.handleReadOnly();
}
this.searchTrigger.closeSearch(true);
return new Promise((resolve, reject) => {
//Before activating, we firstly need to confirm whether the route is coming from peer part - admiral

View File

@ -178,7 +178,7 @@
"REPLICATION": "Replication",
"USERS": "Members",
"LOGS": "Logs",
"LABELS": "labels",
"LABELS": "Labels",
"PROJECTS": "Projects",
"CONFIG": "Configuration"
},
@ -390,6 +390,7 @@
"DELETION_SUMMARY_TAG": "Do you want to delete tag {{param}}?",
"DELETION_TITLE_TAG_DENIED": "Signed tag cannot be deleted",
"DELETION_SUMMARY_TAG_DENIED": "The tag must be removed from the Notary before it can be deleted.\nDelete from Notary via this command:\n{{param}}",
"TAGS_NO_DELETE": "Delete is prohibited in read only mode.",
"FILTER_FOR_REPOSITORIES": "Filter Repositories",
"TAG": "Tag",
"SIZE": "Size",
@ -414,7 +415,7 @@
"INFO": "Info",
"NO_INFO": "No description info for this repository",
"IMAGE": "Images",
"LABELS": ":labels",
"LABELS": "Labels",
"ADD_TO_IMAGE": "Add labels to this image",
"FILTER_BY_LABEL": "Filter projects by label",
"ADD_LABELS": "Add labels",
@ -442,7 +443,7 @@
"AUTH": "Authentication",
"REPLICATION": "Replication",
"EMAIL": "Email",
"LABEL": "Label",
"LABEL": "Labels",
"REPOSITORY": "Repository",
"REPO_READ_ONLY": "Repository Read Only",
"SYSTEM": "System Settings",
@ -487,7 +488,8 @@
"PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project.",
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday.",
"VERIFY_CERT": "Verify Cert from LDAP Server"
"VERIFY_CERT": "Verify Cert from LDAP Server",
"REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image."
},
"LDAP": {
"URL": "LDAP URL",
@ -620,6 +622,7 @@
"OS": "OS",
"SCAN_COMPLETION_TIME": "Scan Completed",
"IMAGE_VULNERABILITIES": "Image Vulnerabilities",
"LEVEL_VULNERABILITIES": "Level Vulnerabilities",
"PLACEHOLDER": "We couldn't find any tags!",
"COPY_ERROR": "Copy failed, please try to manually copy.",
"FILTER_FOR_TAGS": "Filter Tags"
@ -634,7 +637,9 @@
"LABEL_NAME": "Label Name",
"COLOR": "Color",
"FILTER_LABEL_PLACEHOLDER": "Filter Labels",
"NO_LABELS": "No labels"
"NO_LABELS": "No labels",
"DELETION_TITLE_TARGET": "Confirm Label Deletion",
"DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?"
},
"WEEKLY": {
"MONDAY": "Monday",
@ -647,6 +652,7 @@
},
"UNKNOWN_ERROR": "Unknown errors have occurred. Please try again later.",
"UNAUTHORIZED_ERROR": "Your session is invalid or has expired. You need to sign in to continue your action.",
"REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.",
"FORBIDDEN_ERROR": "You do not have the proper privileges to perform the action.",
"GENERAL_ERROR": "Errors have occurred when performing service call: {{param}}.",
"BAD_REQUEST_ERROR": "We are unable to perform your action because of a bad request.",

View File

@ -178,7 +178,7 @@
"REPLICATION": "Replicación",
"USERS": "Miembros",
"LOGS": "Logs",
"LABELS": "labels",
"LABELS": "Labels",
"PROJECTS": "Proyectos",
"CONFIG": "Configuración"
},
@ -390,6 +390,7 @@
"DELETION_SUMMARY_TAG": "¿Quiere eliminar la etiqueta {{param}}?",
"DELETION_TITLE_TAG_DENIED": "La etiqueta firmada no puede ser eliminada",
"DELETION_SUMMARY_TAG_DENIED": "La etiqueta debe ser eliminada de la Notaría antes de eliminarla.\nEliminarla de la Notaría con este comando:\n{{param}}",
"TAGS_NO_DELETE": "Delete is prohibited in read only mode.",
"FILTER_FOR_REPOSITORIES": "Filtrar Repositorios",
"TAG": "Etiqueta",
"SIZE": "Size",
@ -442,7 +443,7 @@
"AUTH": "Autentificación",
"REPLICATION": "Replicación",
"EMAIL": "Email",
"LABEL": "Label",
"LABEL": "Labels",
"REPOSITORY": "Repository",
"REPO_READ_ONLY": "Repository Read Only",
"SYSTEM": "Opciones del Sistema",
@ -487,7 +488,8 @@
"PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos.",
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday.",
"VERIFY_CERT": "Verify Cert from LDAP Server"
"VERIFY_CERT": "Verify Cert from LDAP Server",
"REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image."
},
"LDAP": {
"URL": "LDAP URL",
@ -637,7 +639,9 @@
"LABEL_NAME": "Label Name",
"COLOR": "Color",
"FILTER_Label_PLACEHOLDER": "Filter Labels",
"NO_LABELS": "No labels"
"NO_LABELS": "No labels",
"DELETION_TITLE_TARGET": "Confirm Label Deletion",
"DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?"
},
"WEEKLY": {
"MONDAY": "Monday",
@ -650,6 +654,7 @@
},
"UNKNOWN_ERROR": "Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo más tarde.",
"UNAUTHORIZED_ERROR": "La sesión no es válida o ha caducado. Necesita identificarse de nuevo para llevar a cabo esa acción.",
"REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.",
"FORBIDDEN_ERROR": "No tienes permisos para llevar a cabo esa acción.",
"GENERAL_ERROR": "Han ocurrido errores cuando se llamaba al servicio: {{param}}.",
"BAD_REQUEST_ERROR": "No hemos podido llevar la acción debido a una solicitud incorrecta.",

View File

@ -161,6 +161,7 @@
"REPLICATION": "Réplication",
"USERS": "Membres",
"LOGS": "Logs",
"LABELS": "Labels",
"PROJECTS": "Projets",
"CONFIG": "Configuration"
},
@ -345,6 +346,7 @@
"DELETION_SUMMARY_TAG": "Voulez-vous supprimer le tag {{param}}?",
"DELETION_TITLE_TAG_DENIED": "Un tag signé ne peut être supprimé",
"DELETION_SUMMARY_TAG_DENIED": "La balise doit être supprimée du Résumé avant qu'elle ne puisse être supprimée. \nSupprimer du Résumé via cette commande: \n{{param}}",
"TAGS_NO_DELETE": "Upload/Delete is prohibited in read only mode.",
"FILTER_FOR_REPOSITORIES": "Filtrer les Dépôts",
"TAG": "Tag",
"SIZE": "Taille",
@ -390,7 +392,7 @@
"AUTH": "Identification",
"REPLICATION": "Réplication",
"EMAIL": "Email",
"LABEL": "Label",
"LABEL": "Labels",
"SYSTEM": "Réglages Système",
"CONFIRM_TITLE": "Confirmer pour annuler",
"CONFIRM_SUMMARY": "Certaines modifications n'ont pas été sauvegardées. Voulez-vous les défaire ?",
@ -431,6 +433,7 @@
"PRO_CREATION_RESTRICTION": "L'indicateur pour définir quels utilisateurs ont le droit de créer des projets. Par défaut, tout le monde peut créer un projet. Définissez sur 'Administrateur Seulement' pour que seul un administrateur puisse créer un projet.",
"ROOT_CERT_DOWNLOAD": "Téléchargez le certificat racine du dépôt.",
"SCANNING_POLICY": "Définissez la politique d'analyse des images en fonction des différentes exigences. 'Aucune' : pas de politique active; 'Tousles jours à' : déclenchement du balayage à l'heure spécifiée tous les jours."
"REPO_TOOLTIP": "If true, means maintenance in Progress. During this period, you cannot delete repository, tag and push image."
},
"LDAP": {
"URL": "URL LDAP",
@ -562,10 +565,29 @@
"LABEL": {
"LABEL": "Label",
"DESCRIPTION": "Description",
"CREATION_TIME": "Creation Time"
"CREATION_TIME": "Creation Time",
"NEW_LABEL": "New Label",
"EDIT": "Edit",
"DELETE": "Delete",
"LABEL_NAME": "Label Name",
"COLOR": "Color",
"FILTER_Label_PLACEHOLDER": "Filter Labels",
"NO_LABELS": "No labels",
"DELETION_TITLE_TARGET": "Confirm Label Deletion",
"DELETION_SUMMARY_TARGET": "Do you want to delete {{param}}?"
},
"WEEKLY": {
"MONDAY": "Monday",
"TUESDAY": "Tuesday",
"WEDNESDAY": "Wednesday",
"THURSDAY": "Thursday",
"FRIDAY": "Friday",
"SATURDAY": "Saturday",
"SUNDAY": "Sunday"
},
"UNKNOWN_ERROR": "Des erreurs inconnues sont survenues. Veuillez réessayer plus tard.",
"UNAUTHORIZED_ERROR": "Votre session est invalide ou a expiré. Vous devez vous connecter pour continuer votre action.",
"REPO_READ_ONLY": "Maintenance in Progress: During this period, you cannot delete repository, tag and push image.",
"FORBIDDEN_ERROR": "Vous n'avez pas les privilèges appropriés pour effectuer l'action.",
"GENERAL_ERROR": "Des erreurs sont survenues lors de l'appel à un service : {{param}}.",
"BAD_REQUEST_ERROR": "Nous ne pouvons pas exécuter votre action à cause d'une mauvaise requête.",

View File

@ -390,6 +390,7 @@
"DELETION_SUMMARY_TAG": "确认删除镜像标签 {{param}}?",
"DELETION_TITLE_TAG_DENIED": "已签名的镜像不能被删除",
"DELETION_SUMMARY_TAG_DENIED": "要删除此镜像标签必须首先从Notary中删除。\n请执行如下Notary命令删除:\n{{param}}",
"TAGS_NO_DELETE": "在只读模式下删除是被禁止的",
"FILTER_FOR_REPOSITORIES": "过滤镜像仓库",
"TAG": "标签",
"SIZE": "大小",
@ -487,7 +488,8 @@
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。",
"ROOT_CERT_DOWNLOAD": "下载镜像库根证书.",
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描。",
"VERIFY_CERT": "检查来自LDAP服务端的证书"
"VERIFY_CERT": "检查来自LDAP服务端的证书",
"REPO_TOOLTIP": "选中,表示正在维护状态,不可删除仓库及标签,也不可以推送镜像。"
},
"LDAP": {
"URL": "LDAP URL",
@ -637,7 +639,9 @@
"LABEL_NAME": "标签名字",
"COLOR": "颜色",
"FILTER_Label_PLACEHOLDER": "过滤标签",
"NO_LABELS": "无标签"
"NO_LABELS": "无标签",
"DELETION_TITLE_TARGET":"删除标签确认",
"DELETION_SUMMARY_TARGET": "确认删除标签 {{param}}?"
},
"WEEKLY": {
"MONDAY": "周一",
@ -650,6 +654,7 @@
},
"UNKNOWN_ERROR": "发生未知错误,请稍后再试。",
"UNAUTHORIZED_ERROR": "会话无效或者已经过期, 请重新登录以继续。",
"REPO_READ_ONLY": "正在进行维护,在这期间,不能删除仓库、标签,也不能推送镜像。",
"FORBIDDEN_ERROR": "当前操作被禁止,请确认你有合法的权限。",
"GENERAL_ERROR": "调用后台服务时出现错误: {{param}}。",
"BAD_REQUEST_ERROR": "错误请求, 操作无法完成。",

View File

@ -221,6 +221,7 @@ Set Scan All To Daily
Click Scan Now
click element //vulnerability-config//button[contains(.,'SCAN')]
Enable Read Only
${rc} ${output}= Run And Return Rc And Output curl -u admin:Harbor12345 -s --insecure -H "Content-Type: application/json" -X PUT -d '{"read_only":true}' "https://${ip}/api/configurations"
Log To Console ${output}
@ -229,4 +230,46 @@ Enable Read Only
Disable Read Only
${rc} ${output}= Run And Return Rc And Output curl -u admin:Harbor12345 -s --insecure -H "Content-Type: application/json" -X PUT -d '{"read_only":false}' "https://${ip}/api/configurations"
Log To Console ${output}
Should Be Equal As Integers ${rc} 0
Should Be Equal As Integers ${rc} 0
## System labels
Switch To System Labels
Sleep 1
Click Element xpath=${configuration_xpath}
Click Element xpath=//*[@id="config-label"]
Create New Labels
[Arguments] ${labelname}
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[1]
Sleep 1
Input Text xpath=//*[@id="name"] ${labelname}
Sleep 1
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[2]/clr-dropdown/button
Sleep 1
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[2]/clr-dropdown/clr-dropdown-menu/label[1]
Sleep 1
Input Text xpath=//*[@id="description"] global
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[4]/button[2]
Capture Page Screenshot
Wait Until Page Contains ${labelname}
Update A Label
[Arguments] ${labelname}
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[3]/clr-datagrid/div/div/div/clr-dg-table-wrapper/div[2]/clr-dg-row[1]/div/clr-dg-cell[1]/clr-checkbox
Sleep 1
Click ELement xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[2]
Sleep 1
Input Text xpath=//*[@id="name"] ${labelname}
Sleep 1
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/hbr-create-edit-label/div/form/section/label[4]/button[2]
Capture Page Screenshot
Wait Until Page Contains ${labelname}
Delete A Label
Click Element xpath=//*[@id="system_label"]/hbr-label/div/div/div[3]/clr-datagrid/div/div/div/clr-dg-table-wrapper/div[2]/clr-dg-row[1]/div/clr-dg-cell[1]/clr-checkbox
Sleep 1
Click ELement xpath=//*[@id="system_label"]/hbr-label/div/div/div[2]/button[3]
Sleep 3
Capture Page Screenshot
Click Element xpath=//*[@id="system_label"]/hbr-label/div/confirmation-dialog/clr-modal/div/div[1]/div/div[1]/div/div[3]/button[2]
Wait Until Page Contains Deleted successfully