mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-21 17:55:30 +01:00
Fixes-21021 Display proxy cache in project configuration page (#21048)
Signed-off-by: xuelichao <xuel@vmware.com> Co-authored-by: Lichao Xue <lichao.xue@broadcom.com>
This commit is contained in:
parent
4a5185995e
commit
5ac9733396
@ -16,6 +16,84 @@
|
||||
{{ 'PROJECT_CONFIG.PUBLIC_POLICY' | translate }}
|
||||
</clr-control-helper>
|
||||
</clr-checkbox-container>
|
||||
<clr-checkbox-container *ngIf="isSystemAdmin" clrInline>
|
||||
<label class="label-color-input">
|
||||
{{ 'PROJECT.PROXY_CACHE' | translate }}
|
||||
</label>
|
||||
<clr-checkbox-wrapper id="is-project-proxy-cache-enabled">
|
||||
<input
|
||||
type="checkbox"
|
||||
clrCheckbox
|
||||
disabled="!allowUpdateProxyCacheConfiguration"
|
||||
[(ngModel)]="projectPolicy.ProxyCacheEnabled"
|
||||
name="project-proxy-cache-enabled" />
|
||||
</clr-checkbox-wrapper>
|
||||
<clr-control-helper class="config-subtext">
|
||||
{{ 'PROJECT.PROXY_CACHE_TOOLTIP' | translate }}
|
||||
</clr-control-helper>
|
||||
</clr-checkbox-container>
|
||||
<div
|
||||
*ngIf="isSystemAdmin && projectPolicy.ProxyCacheEnabled"
|
||||
class="clr-form-control mt-0">
|
||||
<label class="clr-control-label"></label>
|
||||
<div class="clr-select-wrapper row-inline">
|
||||
<label class="clr-control-label">
|
||||
{{ 'PROJECT.ENDPOINT' | translate }}
|
||||
</label>
|
||||
<select
|
||||
class="width-164 ml-1"
|
||||
id="registry"
|
||||
name="registry"
|
||||
disabled="!allowUpdateProxyCacheConfiguration"
|
||||
[(ngModel)]="projectPolicy.RegistryId">
|
||||
<option class="display-none" value=""></option>
|
||||
<option *ngFor="let r of registries" [value]="r.id">
|
||||
{{ r.name }}-{{ r.url }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row-inline ml-2" clrInline>
|
||||
<label class="clr-control-label">
|
||||
{{ 'PROJECT.BANDWIDTH' | translate }}
|
||||
</label>
|
||||
<div
|
||||
class="clr-control-container ml-1"
|
||||
[class.clr-error]="bandwidthError">
|
||||
<input
|
||||
type="number"
|
||||
id="bandwidth"
|
||||
[(ngModel)]="projectPolicy.ProxySpeedKb"
|
||||
name="bandwidth"
|
||||
disabled="!allowUpdateProxyCacheConfiguration"
|
||||
class="clr-input width-164 mr-10 clr-input-underline"
|
||||
autocomplete="off"
|
||||
(ngModelChange)="validateBandwidth()" />
|
||||
<clr-icon
|
||||
*ngIf="bandwidthError"
|
||||
class="clr-validate-icon"
|
||||
shape="exclamation-circle"></clr-icon>
|
||||
<div class="clr-select-wrapper mr-10 margin-left-05">
|
||||
<select
|
||||
id="bandwidth_unit"
|
||||
name="bandwidth_unit"
|
||||
disabled="!allowUpdateProxyCacheConfiguration"
|
||||
[(ngModel)]="speedUnit">
|
||||
<option
|
||||
*ngFor="let unit of speedUnits"
|
||||
[value]="unit.UNIT">
|
||||
{{ unit.UNIT }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<clr-control-error
|
||||
*ngIf="bandwidthError"
|
||||
class="tooltip-content">
|
||||
{{ 'PROJECT.SPEED_LIMIT_TIP' | translate }}
|
||||
</clr-control-error>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<clr-checkbox-container *ngIf="!isProxyCacheProject" clrInline>
|
||||
<label
|
||||
><span>{{ 'PROJECT_CONFIG.SECURITY' | translate }}</span></label
|
||||
|
@ -97,3 +97,15 @@
|
||||
.margin-top-05 {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.margin-left-05 {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.row-inline {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.row-inline label {
|
||||
align-self: end;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { of } from 'rxjs';
|
||||
import { SharedTestingModule } from '../../../../shared/shared.module';
|
||||
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { SessionService } from '../../../../shared/services/session.service';
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
|
||||
const mockSystemInfo: SystemInfo[] = [
|
||||
@ -99,6 +100,14 @@ const userPermissionService = {
|
||||
return of(true);
|
||||
},
|
||||
};
|
||||
|
||||
const sessionService = {
|
||||
getCurrentUser() {
|
||||
return of({
|
||||
has_admin_role: true,
|
||||
});
|
||||
},
|
||||
};
|
||||
describe('ProjectPolicyConfigComponent', () => {
|
||||
let fixture: ComponentFixture<TestHostComponent>,
|
||||
component: TestHostComponent;
|
||||
@ -114,6 +123,7 @@ describe('ProjectPolicyConfigComponent', () => {
|
||||
{ provide: ErrorHandler, useClass: MessageHandlerService },
|
||||
{ provide: ProjectService, useValue: projectService },
|
||||
{ provide: SystemInfoService, useValue: systemInfoService },
|
||||
{ provide: SessionService, useValue: sessionService },
|
||||
{
|
||||
provide: UserPermissionService,
|
||||
useValue: userPermissionService,
|
||||
|
@ -4,6 +4,7 @@ import { ProjectService } from '../../../../shared/services';
|
||||
import { ErrorHandler } from '../../../../shared/units/error-handler';
|
||||
import { State, SystemCVEAllowlist } from '../../../../shared/services';
|
||||
import {
|
||||
BandwidthUnit,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
} from '../../../../shared/entities/shared.const';
|
||||
@ -15,10 +16,15 @@ import { Project } from './project';
|
||||
import { SystemInfo, SystemInfoService } from '../../../../shared/services';
|
||||
import { UserPermissionService } from '../../../../shared/services';
|
||||
import { USERSTATICPERMISSION } from '../../../../shared/services';
|
||||
import { SessionService } from '../../../../shared/services/session.service';
|
||||
import { Registry } from '../../../../../../ng-swagger-gen/models/registry';
|
||||
import {
|
||||
EventService,
|
||||
HarborEvent,
|
||||
} from '../../../../services/event-service/event.service';
|
||||
import { forkJoin, Observable } from 'rxjs';
|
||||
import { MessageHandlerService } from 'src/app/shared/services/message-handler.service';
|
||||
import { RegistryService } from 'ng-swagger-gen/services';
|
||||
|
||||
const ONE_THOUSAND: number = 1000;
|
||||
const LOW: string = 'low';
|
||||
@ -33,6 +39,9 @@ export class ProjectPolicy {
|
||||
PreventVulImgSeverity: string;
|
||||
ScanImgOnPush: boolean;
|
||||
GenerateSbomOnPush: boolean;
|
||||
ProxyCacheEnabled: boolean;
|
||||
RegistryId?: number | null;
|
||||
ProxySpeedKb?: number | null;
|
||||
|
||||
constructor() {
|
||||
this.Public = false;
|
||||
@ -42,6 +51,9 @@ export class ProjectPolicy {
|
||||
this.PreventVulImgSeverity = LOW;
|
||||
this.ScanImgOnPush = false;
|
||||
this.GenerateSbomOnPush = false;
|
||||
this.ProxyCacheEnabled = false;
|
||||
this.RegistryId = null;
|
||||
this.ProxySpeedKb = -1;
|
||||
}
|
||||
|
||||
initByProject(pro: Project) {
|
||||
@ -55,8 +67,14 @@ export class ProjectPolicy {
|
||||
}
|
||||
this.ScanImgOnPush = pro.metadata.auto_scan === 'true';
|
||||
this.GenerateSbomOnPush = pro.metadata.auto_sbom_generation === 'true';
|
||||
this.ProxyCacheEnabled = pro.registry_id ? true : false;
|
||||
this.RegistryId = pro.registry_id;
|
||||
this.ProxySpeedKb = pro.metadata.proxy_speed_kb
|
||||
? pro.metadata.proxy_speed_kb
|
||||
: -1;
|
||||
}
|
||||
}
|
||||
const PAGE_SIZE: number = 100;
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-project-policy-config',
|
||||
@ -65,6 +83,7 @@ export class ProjectPolicy {
|
||||
})
|
||||
export class ProjectPolicyConfigComponent implements OnInit {
|
||||
onGoing = false;
|
||||
allowUpdateProxyCacheConfiguration = false;
|
||||
@Input() projectId: number;
|
||||
@Input() projectName = 'unknown';
|
||||
@Input() isProxyCacheProject: boolean = false;
|
||||
@ -81,6 +100,7 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
orgProjectPolicy = new ProjectPolicy();
|
||||
projectPolicy = new ProjectPolicy();
|
||||
hasChangeConfigRole: boolean;
|
||||
|
||||
severityOptions = [
|
||||
{
|
||||
severity: 'critical',
|
||||
@ -102,6 +122,20 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
systemAllowlistOrProjectAllowlistOrigin: string;
|
||||
projectAllowlist;
|
||||
projectAllowlistOrigin;
|
||||
speedUnit = BandwidthUnit.KB;
|
||||
speedUnits = [
|
||||
{
|
||||
UNIT: BandwidthUnit.KB,
|
||||
},
|
||||
{
|
||||
UNIT: BandwidthUnit.MB,
|
||||
},
|
||||
];
|
||||
// **Added property for bandwidth error message**
|
||||
bandwidthError: string | null = null;
|
||||
registries: Registry[] = [];
|
||||
supportedRegistryTypeQueryString: string =
|
||||
'type={docker-hub harbor azure-acr aws-ecr google-gcr quay docker-registry github-ghcr jfrog-artifactory}';
|
||||
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
@ -109,6 +143,9 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
private projectService: ProjectService,
|
||||
private systemInfoService: SystemInfoService,
|
||||
private userPermission: UserPermissionService,
|
||||
private session: SessionService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private endpointService: RegistryService,
|
||||
private event: EventService
|
||||
) {}
|
||||
|
||||
@ -135,6 +172,74 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
this.retrieve();
|
||||
this.getPermission();
|
||||
this.getSystemAllowlist();
|
||||
if (this.isSystemAdmin) {
|
||||
this.getRegistries();
|
||||
}
|
||||
}
|
||||
|
||||
validateBandwidth(): void {
|
||||
const value = Number(this.projectPolicy.ProxySpeedKb);
|
||||
if (
|
||||
isNaN(value) ||
|
||||
(!Number.isInteger(value) && value !== -1) ||
|
||||
(value <= 0 && value !== -1)
|
||||
) {
|
||||
this.bandwidthError =
|
||||
'Please enter -1 or an integer greater than 0.';
|
||||
} else {
|
||||
this.bandwidthError = null;
|
||||
}
|
||||
}
|
||||
|
||||
getRegistries() {
|
||||
this.endpointService
|
||||
.listRegistriesResponse({
|
||||
page: 1,
|
||||
pageSize: PAGE_SIZE,
|
||||
q: this.supportedRegistryTypeQueryString,
|
||||
})
|
||||
.subscribe(
|
||||
result => {
|
||||
// Get total count
|
||||
if (result.headers) {
|
||||
const xHeader: string =
|
||||
result.headers.get('X-Total-Count');
|
||||
const totalCount = parseInt(xHeader, 0);
|
||||
let arr = result.body || [];
|
||||
if (totalCount <= PAGE_SIZE) {
|
||||
// already gotten all Registries
|
||||
this.registries = result.body || [];
|
||||
} else {
|
||||
// get all the registries in specified times
|
||||
const times: number = Math.ceil(
|
||||
totalCount / PAGE_SIZE
|
||||
);
|
||||
const observableList: Observable<Registry[]>[] = [];
|
||||
for (let i = 2; i <= times; i++) {
|
||||
observableList.push(
|
||||
this.endpointService.listRegistries({
|
||||
page: i,
|
||||
pageSize: PAGE_SIZE,
|
||||
q: this
|
||||
.supportedRegistryTypeQueryString,
|
||||
})
|
||||
);
|
||||
}
|
||||
forkJoin(observableList).subscribe(res => {
|
||||
if (res && res.length) {
|
||||
res.forEach(item => {
|
||||
arr = arr.concat(item);
|
||||
});
|
||||
this.registries = arr;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.error(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getSystemAllowlist() {
|
||||
@ -171,6 +276,11 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public get isSystemAdmin(): boolean {
|
||||
let account = this.session.getCurrentUser();
|
||||
return account != null && account.has_admin_role;
|
||||
}
|
||||
|
||||
retrieve(state?: State): any {
|
||||
this.projectService.getProject(this.projectId).subscribe(
|
||||
response => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
export class Project {
|
||||
project_id: number;
|
||||
owner_id?: number;
|
||||
registry_id?: number | null;
|
||||
name: string;
|
||||
creation_time?: Date | string;
|
||||
deleted?: number;
|
||||
@ -21,6 +22,7 @@ export class Project {
|
||||
auto_scan: string | boolean;
|
||||
auto_sbom_generation: string | boolean;
|
||||
reuse_sys_cve_allowlist?: string;
|
||||
proxy_speed_kb?: number | null;
|
||||
};
|
||||
cve_allowlist?: object;
|
||||
constructor() {
|
||||
@ -30,5 +32,6 @@ export class Project {
|
||||
this.metadata.severity = 'low';
|
||||
this.metadata.auto_scan = false;
|
||||
this.metadata.auto_sbom_generation = false;
|
||||
this.metadata.proxy_speed_kb = -1;
|
||||
}
|
||||
}
|
||||
|
@ -144,6 +144,7 @@ export class ProjectDefaultService extends ProjectService {
|
||||
.put<any>(
|
||||
`${baseUrl}/${projectId}`,
|
||||
{
|
||||
registry_id: projectPolicy.RegistryId,
|
||||
metadata: {
|
||||
public: projectPolicy.Public ? 'true' : 'false',
|
||||
enable_content_trust: projectPolicy.ContentTrust
|
||||
@ -162,6 +163,7 @@ export class ProjectDefaultService extends ProjectService {
|
||||
? 'true'
|
||||
: 'false',
|
||||
reuse_sys_cve_allowlist: reuseSysCVEVAllowlist,
|
||||
proxy_speed_kb: projectPolicy.ProxySpeedKb.toString(),
|
||||
},
|
||||
cve_allowlist: projectAllowlist,
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user