Support clair db timestamps (#2767)

* refine the test case of scheduler

* Fix bug

* Support root cert downloaded

* Fix code conflicts

* support clair db timestamps
This commit is contained in:
Steven Zou 2017-07-13 13:04:42 +08:00 committed by Yan
parent ca6bd3b585
commit 0ce74dd377
15 changed files with 127 additions and 16 deletions

View File

@ -0,0 +1,9 @@
export const REGISTRY_CONFIG_STYLES: string = `
.info-tips-icon {
color: grey;
}
.info-tips-icon:hover {
color: #007CBB;
}
`;

View File

@ -2,7 +2,7 @@ export const REGISTRY_CONFIG_HTML: string = `
<div> <div>
<replication-config #replicationConfig [(replicationConfig)]="config" [showSubTitle]="true"></replication-config> <replication-config #replicationConfig [(replicationConfig)]="config" [showSubTitle]="true"></replication-config>
<system-settings #systemSettings [(systemSettings)]="config" [showSubTitle]="true" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings> <system-settings #systemSettings [(systemSettings)]="config" [showSubTitle]="true" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>
<vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true"></vulnerability-config> <vulnerability-config *ngIf="withClair" #vulnerabilityConfig [(vulnerabilityConfig)]="config" [showSubTitle]="true" [clairDBStatus]="clairDB"></vulnerability-config>
<div> <div>
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="shouldDisable">{{'BUTTON.SAVE' | translate}}</button> <button type="button" class="btn btn-primary" (click)="save()" [disabled]="shouldDisable">{{'BUTTON.SAVE' | translate}}</button>
<button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="shouldDisable">{{'BUTTON.CANCEL' | translate}}</button> <button type="button" class="btn btn-outline" (click)="cancel()" [disabled]="shouldDisable">{{'BUTTON.CANCEL' | translate}}</button>

View File

@ -2,7 +2,7 @@ import { Component, OnInit, EventEmitter, Output, ViewChild, Input } from '@angu
import { Configuration, ComplexValueItem } from './config'; import { Configuration, ComplexValueItem } from './config';
import { REGISTRY_CONFIG_HTML } from './registry-config.component.html'; import { REGISTRY_CONFIG_HTML } from './registry-config.component.html';
import { ConfigurationService, SystemInfoService, SystemInfo } from '../service/index'; import { ConfigurationService, SystemInfoService, SystemInfo, ClairDBStatus } from '../service/index';
import { toPromise } from '../utils'; import { toPromise } from '../utils';
import { ErrorHandler } from '../error-handler'; import { ErrorHandler } from '../error-handler';
import { import {
@ -54,6 +54,11 @@ export class RegistryConfigComponent implements OnInit {
return this.systemInfo && this.systemInfo.with_clair; return this.systemInfo && this.systemInfo.with_clair;
} }
get clairDB(): ClairDBStatus {
return this.systemInfo && this.systemInfo.clair_vulnerability_status ?
this.systemInfo.clair_vulnerability_status : null;
}
ngOnInit(): void { ngOnInit(): void {
//Get system info //Get system info
toPromise<SystemInfo>(this.systemInfoService.getSystemInfo()) toPromise<SystemInfo>(this.systemInfoService.getSystemInfo())

View File

@ -3,10 +3,12 @@ import { NgForm } from '@angular/forms';
import { REPLICATION_CONFIG_HTML } from './replication-config.component.html'; import { REPLICATION_CONFIG_HTML } from './replication-config.component.html';
import { Configuration } from '../config'; import { Configuration } from '../config';
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
@Component({ @Component({
selector: 'replication-config', selector: 'replication-config',
template: REPLICATION_CONFIG_HTML template: REPLICATION_CONFIG_HTML,
styles: [REGISTRY_CONFIG_STYLES]
}) })
export class ReplicationConfigComponent { export class ReplicationConfigComponent {
config: Configuration; config: Configuration;

View File

@ -3,10 +3,12 @@ import { NgForm } from '@angular/forms';
import { SYSTEM_SETTINGS_HTML } from './system-settings.component.html'; import { SYSTEM_SETTINGS_HTML } from './system-settings.component.html';
import { Configuration } from '../config'; import { Configuration } from '../config';
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
@Component({ @Component({
selector: 'system-settings', selector: 'system-settings',
template: SYSTEM_SETTINGS_HTML template: SYSTEM_SETTINGS_HTML,
styles: [REGISTRY_CONFIG_STYLES]
}) })
export class SystemSettingsComponent { export class SystemSettingsComponent {
config: Configuration; config: Configuration;

View File

@ -2,6 +2,21 @@ export const VULNERABILITY_CONFIG_HTML: string = `
<form #systemConfigFrom="ngForm" class="compact"> <form #systemConfigFrom="ngForm" class="compact">
<section class="form-block" style="margin-top:0px;margin-bottom:0px;"> <section class="form-block" style="margin-top:0px;margin-bottom:0px;">
<label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label> <label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
<div class="form-group">
<label>{{ 'CONFIG.SCANNING.DB_REFRESH_TIME' | translate }}</label>
<clr-dropdown [clrMenuPosition]="'bottom-right'" style="margin-top:-8px;" class="clr-dropdown-override">
<button class="btn btn-link btn-font" clrDropdownToggle>
{{ updatedTimestamp }}
<clr-icon shape="caret down"></clr-icon>
</button>
<div class="dropdown-menu" style="min-width:300px;">
<div *ngFor="let nt of namespaceTimestamps" class="namespace">
<span class="label label-info">{{nt.namespace}}</span>
<span>{{convertToLocalTime(nt.last_update*1000)}}</span>
</div>
</div>
</clr-dropdown>
</div>
<div class="form-group"> <div class="form-group">
<label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label> <label for="scanAllPolicy">{{ 'CONFIG.SCANNING.SCAN_ALL' | translate }}</label>
<div class="select"> <div class="select">
@ -33,4 +48,16 @@ export const VULNERABILITY_CONFIG_STYLES: string = `
font-size: 14px !important; font-size: 14px !important;
font-weight: 600 !important; font-weight: 600 !important;
} }
.btn-font {
font-size: 14px !important;
}
.namespace {
margin-left: 24px;
}
.clr-dropdown-override {
margin-top: -8px;
}
`; `;

View File

@ -7,6 +7,9 @@ import { ScanningResultService } from '../../service/scanning.service';
import { ErrorHandler } from '../../error-handler'; import { ErrorHandler } from '../../error-handler';
import { toPromise } from '../../utils'; import { toPromise } from '../../utils';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { ClairDBStatus, ClairDetail } from '../../service/interface';
import { REGISTRY_CONFIG_STYLES } from '../registry-config.component.css';
const ONE_HOUR_SECONDS: number = 3600; const ONE_HOUR_SECONDS: number = 3600;
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS; const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
@ -14,12 +17,13 @@ const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
@Component({ @Component({
selector: 'vulnerability-config', selector: 'vulnerability-config',
template: VULNERABILITY_CONFIG_HTML, template: VULNERABILITY_CONFIG_HTML,
styles: [VULNERABILITY_CONFIG_STYLES] styles: [VULNERABILITY_CONFIG_STYLES, REGISTRY_CONFIG_STYLES]
}) })
export class VulnerabilityConfigComponent { export class VulnerabilityConfigComponent {
_localTime: Date = new Date(); _localTime: Date = new Date();
config: Configuration; config: Configuration;
openState: boolean = false;
@Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>(); @Output() configChange: EventEmitter<Configuration> = new EventEmitter<Configuration>();
@Input() @Input()
@ -41,7 +45,26 @@ export class VulnerabilityConfigComponent {
this.configChange.emit(this.config); this.configChange.emit(this.config);
} }
@Input() showSubTitle: boolean = false @Input() showSubTitle: boolean = false;
@Input() clairDBStatus: ClairDBStatus;
get updatedTimestamp(): string {
if (this.clairDBStatus && this.clairDBStatus.overall_last_update > 0) {
return this.convertToLocalTime(this.clairDBStatus.overall_last_update*1000);
}
return "--";
}
get namespaceTimestamps(): ClairDetail[] {
if (this.clairDBStatus &&
this.clairDBStatus.details &&
this.clairDBStatus.details.length > 0) {
return this.clairDBStatus.details;
}
return [];
}
//UTC time //UTC time
get dailyTime(): string { get dailyTime(): string {
@ -188,6 +211,14 @@ export class VulnerabilityConfigComponent {
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private translate: TranslateService) { } private translate: TranslateService) { }
convertToLocalTime(utcTime: number): string {
let offset: number = this._localTime.getTimezoneOffset() * 60;
let timeWithLocal: number = utcTime - offset;
let dt = new Date();
dt.setTime(timeWithLocal);
return dt.toLocaleString();
}
scanNow(): void { scanNow(): void {
toPromise<any>(this.scanningService.startScanningAll()) toPromise<any>(this.scanningService.startScanningAll())
.then(() => { .then(() => {

View File

@ -155,6 +155,23 @@ export interface SystemInfo {
self_registration?: boolean; self_registration?: boolean;
has_ca_root?: boolean; has_ca_root?: boolean;
harbor_version?: string; harbor_version?: string;
clair_vulnerability_status?: ClairDBStatus;
}
/**
* Clair database status info.
*
* @export
* @interface ClairDetail
*/
export interface ClairDetail {
namespace: string;
last_update: number;
}
export interface ClairDBStatus {
overall_last_update: number;
details: ClairDetail[];
} }
export enum VulnerabilitySeverity { export enum VulnerabilitySeverity {

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8", "clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8", "clarity-ui": "^0.9.8",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"harbor-ui": "^0.2.55", "harbor-ui": "~0.2.63",
"intl": "^1.2.5", "intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2", "mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0", "ngx-cookie": "^1.0.0",
@ -68,4 +68,4 @@
"typings": "^1.4.0", "typings": "^1.4.0",
"webdriver-manager": "10.2.5" "webdriver-manager": "10.2.5"
} }
} }

View File

@ -11,8 +11,10 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { ClairDBStatus } from 'harbor-ui';
export class AppConfig { export class AppConfig {
constructor(){ constructor() {
//Set default value //Set default value
this.with_notary = false; this.with_notary = false;
this.with_admiral = false; this.with_admiral = false;
@ -23,9 +25,13 @@ export class AppConfig {
this.project_creation_restriction = "everyone"; this.project_creation_restriction = "everyone";
this.self_registration = true; this.self_registration = true;
this.has_ca_root = false; this.has_ca_root = false;
this.harbor_version = "0.5.0";//default this.harbor_version = "1.2.0";//default
this.clair_vulnerability_status = {
overall_last_update: 0,
details: []
};
} }
with_notary: boolean; with_notary: boolean;
with_admiral: boolean; with_admiral: boolean;
with_clair: boolean; with_clair: boolean;
@ -36,4 +42,5 @@ export class AppConfig {
self_registration: boolean; self_registration: boolean;
has_ca_root: boolean; has_ca_root: boolean;
harbor_version: string; harbor_version: string;
clair_vulnerability_status?: ClairDBStatus;
} }

View File

@ -32,7 +32,7 @@
<system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings> <system-settings [(systemSettings)]="allConfig" [hasAdminRole]="hasAdminRole" [hasCAFile]="hasCAFile"></system-settings>
</section> </section>
<section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'> <section id="vulnerability" *ngIf="withClair" role="tabpanel" aria-labelledby="config-vulnerability" [hidden]='!isCurrentTabContent("vulnerability")'>
<vulnerability-config [(vulnerabilityConfig)]="allConfig"></vulnerability-config> <vulnerability-config [(vulnerabilityConfig)]="allConfig" [clairDBStatus]="clairDB"></vulnerability-config>
</section> </section>
<div> <div>
<button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button> <button type="button" class="btn btn-primary" (click)="save()" [disabled]="!isValid() || !hasChanges()">{{'BUTTON.SAVE' | translate}}</button>

View File

@ -32,7 +32,8 @@ import {
ComplexValueItem, ComplexValueItem,
ReplicationConfigComponent, ReplicationConfigComponent,
SystemSettingsComponent, SystemSettingsComponent,
VulnerabilityConfigComponent VulnerabilityConfigComponent,
ClairDBStatus
} from 'harbor-ui'; } from 'harbor-ui';
const fakePass = "aWpLOSYkIzJTTU4wMDkx"; const fakePass = "aWpLOSYkIzJTTU4wMDkx";
@ -84,6 +85,10 @@ export class ConfigurationComponent implements OnInit, OnDestroy {
return this.appConfigService.getConfig().with_clair; return this.appConfigService.getConfig().with_clair;
} }
public get clairDB(): ClairDBStatus {
return this.appConfigService.getConfig().clair_vulnerability_status;
}
isCurrentTabLink(tabId: string): boolean { isCurrentTabLink(tabId: string): boolean {
return this.currentTabId === tabId; return this.currentTabId === tabId;
} }

View File

@ -413,7 +413,9 @@
"SCAN_NOW": "SCAN NOW", "SCAN_NOW": "SCAN NOW",
"NONE_POLICY": "None", "NONE_POLICY": "None",
"DAILY_POLICY": "Daily At", "DAILY_POLICY": "Daily At",
"REFRESH_POLICY": "Upon Refresh" "REFRESH_POLICY": "Upon Refresh",
"DB_REFRESH_TIME": "Database updated on",
"DB_NOT_READY": "Vulnerability database might not be fully ready!"
}, },
"TEST_MAIL_SUCCESS": "Connection to mail server is verified.", "TEST_MAIL_SUCCESS": "Connection to mail server is verified.",
"TEST_LDAP_SUCCESS": "Connection to LDAP server is verified.", "TEST_LDAP_SUCCESS": "Connection to LDAP server is verified.",

View File

@ -414,7 +414,9 @@
"SCAN_NOW": "SCAN NOW", "SCAN_NOW": "SCAN NOW",
"NONE_POLICY": "None", "NONE_POLICY": "None",
"DAILY_POLICY": "Daily At", "DAILY_POLICY": "Daily At",
"REFRESH_POLICY": "Upon Refresh" "REFRESH_POLICY": "Upon Refresh",
"DB_REFRESH_TIME": "Database updated on",
"DB_NOT_READY": "Vulnerability database might not be fully ready!"
}, },
"TEST_MAIL_SUCCESS": "La conexión al servidor de correo ha sido verificada.", "TEST_MAIL_SUCCESS": "La conexión al servidor de correo ha sido verificada.",
"TEST_LDAP_SUCCESS": "La conexión al servidor LDAP ha sido verificada.", "TEST_LDAP_SUCCESS": "La conexión al servidor LDAP ha sido verificada.",

View File

@ -413,7 +413,9 @@
"SCAN_NOW": "开始扫描", "SCAN_NOW": "开始扫描",
"NONE_POLICY": "无", "NONE_POLICY": "无",
"DAILY_POLICY": "每日定时", "DAILY_POLICY": "每日定时",
"REFRESH_POLICY": "缺陷库刷新后" "REFRESH_POLICY": "缺陷库刷新后",
"DB_REFRESH_TIME": "数据库更新于",
"DB_NOT_READY": "缺陷数据库可能没有完全准备好!"
}, },
"TEST_MAIL_SUCCESS": "邮件服务器的连通正常。", "TEST_MAIL_SUCCESS": "邮件服务器的连通正常。",
"TEST_LDAP_SUCCESS": "LDAP服务器的连通正常。", "TEST_LDAP_SUCCESS": "LDAP服务器的连通正常。",