mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-30 06:03:45 +01:00
Update for revised tags API.
This commit is contained in:
parent
924a62df7e
commit
2f21ab8442
@ -16,7 +16,6 @@ import { Http, URLSearchParams, Response } from '@angular/http';
|
|||||||
|
|
||||||
import { Repository } from './repository';
|
import { Repository } from './repository';
|
||||||
import { Tag } from './tag';
|
import { Tag } from './tag';
|
||||||
import { VerifiedSignature } from './verified-signature';
|
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable'
|
import { Observable } from 'rxjs/Observable'
|
||||||
import 'rxjs/add/observable/of';
|
import 'rxjs/add/observable/of';
|
||||||
@ -46,35 +45,6 @@ export class RepositoryService {
|
|||||||
.catch(error=>Observable.throw(error));
|
.catch(error=>Observable.throw(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
listNotarySignatures(repoName: string): Observable<VerifiedSignature[]> {
|
|
||||||
return this.http
|
|
||||||
.get(`/api/repositories/${repoName}/signatures`)
|
|
||||||
.map(response=>response.json())
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
listTagsWithVerifiedSignatures(repoName: string): Observable<Tag[]> {
|
|
||||||
return this.listTags(repoName)
|
|
||||||
.map(res=>res)
|
|
||||||
.flatMap(tags=>{
|
|
||||||
return this.listNotarySignatures(repoName).map(signatures=>{
|
|
||||||
tags.forEach(t=>{
|
|
||||||
for(let i = 0; i < signatures.length; i++) {
|
|
||||||
if(signatures[i].tag === t.tag) {
|
|
||||||
t.signed = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return tags;
|
|
||||||
})
|
|
||||||
.catch(error=>{
|
|
||||||
return Observable.of(tags);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.catch(error=>Observable.throw(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteRepository(repoName: string): Observable<any> {
|
deleteRepository(repoName: string): Observable<any> {
|
||||||
return this.http
|
return this.http
|
||||||
.delete(`/api/repositories/${repoName}`)
|
.delete(`/api/repositories/${repoName}`)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
<h3 class="modal-title">{{ manifestInfoTitle | translate }}</h3>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row col-md-12">
|
<div class="row col-md-12">
|
||||||
<textarea rows="3" (click)="selectAndCopy($event)">{{tagID}}</textarea>
|
<textarea rows="3" (click)="selectAndCopy($event)">{{digestId}}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -24,25 +24,20 @@
|
|||||||
<clr-dg-column>{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.DOCKER_VERSION' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'REPOSITORY.ARCHITECTURE' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.ARCHITECTURE' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column>{{'REPOSITORY.OS' | translate}}</clr-dg-column>
|
<clr-dg-column>{{'REPOSITORY.OS' | translate}}</clr-dg-column>
|
||||||
<clr-dg-row *clrDgItems="let t of tags" [clrDgItem]='t'>
|
<clr-dg-row *clrDgItems="let t of tags">
|
||||||
<clr-dg-action-overflow>
|
<clr-dg-action-overflow>
|
||||||
<button class="action-item" (click)="showTagID('tag', t)">{{'REPOSITORY.COPY_ID' | translate}}</button>
|
<button class="action-item" (click)="showDigestId(t)">{{'REPOSITORY.COPY_DIGEST_ID' | translate}}</button>
|
||||||
<button class="action-item" (click)="showTagID('parent', t)">{{'REPOSITORY.COPY_PARENT_ID' | translate}}</button>
|
|
||||||
<button class="action-item" [hidden]="!hasProjectAdminRole" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</button>
|
<button class="action-item" [hidden]="!hasProjectAdminRole" (click)="deleteTag(t)">{{'REPOSITORY.DELETE' | translate}}</button>
|
||||||
</clr-dg-action-overflow>
|
</clr-dg-action-overflow>
|
||||||
<clr-dg-cell>{{t.tag}}</clr-dg-cell>
|
<clr-dg-cell>{{t.name}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.pullCommand}}</clr-dg-cell>
|
<clr-dg-cell>docker pull {{registryUrl}}/{{repoName}}:{{t.name}}</clr-dg-cell>
|
||||||
<clr-dg-cell *ngIf="withNotary" [ngSwitch]="t.signed">
|
<clr-dg-cell *ngIf="withNotary">
|
||||||
<clr-icon shape="check" *ngSwitchCase="1" style="color: #1D5100;"></clr-icon>
|
<clr-icon *ngIf="t.signature" shape="check" style="color: #1D5100;"></clr-icon>
|
||||||
<clr-icon shape="close" *ngSwitchCase="0" style="color: #C92100;"></clr-icon>
|
<clr-icon *ngIf="!t.signature" shape="close" style="color: #C92100;"></clr-icon>
|
||||||
<a href="javascript:void(0)" *ngSwitchDefault role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right">
|
|
||||||
<clr-icon shape="help" style="color: #565656;" size="16"></clr-icon>
|
|
||||||
<span class="tooltip-content">{{'REPOSITORY.NOTARY_IS_UNDETERMINED' | translate}}</span>
|
|
||||||
</a>
|
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.author}}</clr-dg-cell>
|
<clr-dg-cell>{{t.author}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.created | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>{{t.created | date: 'short'}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.dockerVersion}}</clr-dg-cell>
|
<clr-dg-cell>{{t.docker_version}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
<clr-dg-cell>{{t.os}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
|
@ -24,7 +24,6 @@ import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmati
|
|||||||
import { Subscription } from 'rxjs/Subscription';
|
import { Subscription } from 'rxjs/Subscription';
|
||||||
|
|
||||||
import { Tag } from '../tag';
|
import { Tag } from '../tag';
|
||||||
import { TagView } from '../tag-view';
|
|
||||||
|
|
||||||
import { AppConfigService } from '../../app-config.service';
|
import { AppConfigService } from '../../app-config.service';
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
hasProjectAdminRole: boolean = false;
|
hasProjectAdminRole: boolean = false;
|
||||||
|
|
||||||
tags: TagView[];
|
tags: Tag[];
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
withNotary: boolean;
|
withNotary: boolean;
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
showTagManifestOpened: boolean;
|
showTagManifestOpened: boolean;
|
||||||
manifestInfoTitle: string;
|
manifestInfoTitle: string;
|
||||||
tagID: string;
|
digestId: string;
|
||||||
staticBackdrop: boolean = true;
|
staticBackdrop: boolean = true;
|
||||||
closable: boolean = false;
|
closable: boolean = false;
|
||||||
|
|
||||||
@ -79,9 +78,8 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
if (tag.signed) {
|
if (tag.signed) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
let tagName = tag.tag;
|
|
||||||
this.repositoryService
|
this.repositoryService
|
||||||
.deleteRepoByTag(this.repoName, tagName)
|
.deleteRepoByTag(this.repoName, tag.name)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
response => {
|
response => {
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
@ -103,10 +101,11 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
this.projectId = this.route.snapshot.params['id'];
|
this.projectId = this.route.snapshot.params['id'];
|
||||||
this.repoName = this.route.snapshot.params['repo'];
|
this.repoName = this.route.snapshot.params['repo'];
|
||||||
this.tags = [];
|
|
||||||
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
||||||
this.withNotary = this.appConfigService.getConfig().with_notary;
|
this.withNotary = this.appConfigService.getConfig().with_notary;
|
||||||
this.retrieve();
|
this.retrieve();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -120,63 +119,25 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
this.repositoryService
|
this.repositoryService
|
||||||
.listTags(this.repoName)
|
.listTags(this.repoName)
|
||||||
.subscribe(
|
.subscribe(
|
||||||
items => this.listTags(items),
|
tags => this.tags = tags,
|
||||||
error => this.messageHandlerService.handleError(error));
|
error => this.messageHandlerService.handleError(error));
|
||||||
|
|
||||||
if(this.withNotary) {
|
|
||||||
this.repositoryService
|
|
||||||
.listNotarySignatures(this.repoName)
|
|
||||||
.subscribe(
|
|
||||||
signatures => {
|
|
||||||
this.tags.forEach((t, n)=>{
|
|
||||||
let signed = false;
|
|
||||||
for(let i = 0; i < signatures.length; i++) {
|
|
||||||
if (signatures[i].tag === t.tag) {
|
|
||||||
signed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.tags[n].signed = (signed) ? 1 : 0;
|
|
||||||
this.ref.markForCheck();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error => console.error('Cannot determine the signature of this tag.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listTags(tags: Tag[]): void {
|
|
||||||
tags.forEach(t => {
|
|
||||||
let tag = new TagView();
|
|
||||||
tag.tag = t.tag;
|
|
||||||
let data = JSON.parse(t.manifest.history[0].v1Compatibility);
|
|
||||||
tag.architecture = data['architecture'];
|
|
||||||
tag.author = data['author'];
|
|
||||||
tag.signed = t.signed;
|
|
||||||
tag.created = data['created'];
|
|
||||||
tag.dockerVersion = data['docker_version'];
|
|
||||||
tag.pullCommand = 'docker pull ' + this.registryUrl + '/' + t.manifest.name + ':' + t.tag;
|
|
||||||
tag.os = data['os'];
|
|
||||||
tag.id = data['id'];
|
|
||||||
tag.parent = data['parent'];
|
|
||||||
this.tags.push(tag);
|
|
||||||
});
|
|
||||||
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
|
let hnd = setInterval(()=>this.ref.markForCheck(), 100);
|
||||||
setTimeout(()=>clearInterval(hnd), 1000);
|
setTimeout(()=>clearInterval(hnd), 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteTag(tag: TagView) {
|
deleteTag(tag: Tag) {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
let titleKey: string, summaryKey: string, content: string, buttons: ConfirmationButtons;
|
let titleKey: string, summaryKey: string, content: string, buttons: ConfirmationButtons;
|
||||||
if (tag.signed) {
|
if (tag.signature) {
|
||||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED';
|
titleKey = 'REPOSITORY.DELETION_TITLE_TAG_DENIED';
|
||||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED';
|
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG_DENIED';
|
||||||
buttons = ConfirmationButtons.CLOSE;
|
buttons = ConfirmationButtons.CLOSE;
|
||||||
content = 'notary -s https://' + this.registryUrl + ':4443 -d ~/.docker/trust remove -p ' + this.registryUrl + '/' + this.repoName + ' ' + tag.tag;
|
content = 'notary -s https://' + this.registryUrl + ':4443 -d ~/.docker/trust remove -p ' + this.registryUrl + '/' + this.repoName + ' ' + tag.name;
|
||||||
} else {
|
} else {
|
||||||
titleKey = 'REPOSITORY.DELETION_TITLE_TAG';
|
titleKey = 'REPOSITORY.DELETION_TITLE_TAG';
|
||||||
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG';
|
summaryKey = 'REPOSITORY.DELETION_SUMMARY_TAG';
|
||||||
buttons = ConfirmationButtons.DELETE_CANCEL;
|
buttons = ConfirmationButtons.DELETE_CANCEL;
|
||||||
content = tag.tag;
|
content = tag.name;
|
||||||
}
|
}
|
||||||
let message = new ConfirmationMessage(
|
let message = new ConfirmationMessage(
|
||||||
titleKey,
|
titleKey,
|
||||||
@ -189,15 +150,10 @@ export class TagRepositoryComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
showTagID(type: string, tag: TagView) {
|
showDigestId(tag: Tag) {
|
||||||
if(tag) {
|
if(tag) {
|
||||||
if(type === 'tag') {
|
this.manifestInfoTitle = 'REPOSITORY.COPY_DIGEST_ID';
|
||||||
this.manifestInfoTitle = 'REPOSITORY.COPY_ID';
|
this.digestId = tag.digest;
|
||||||
this.tagID = tag.id;
|
|
||||||
} else if(type === 'parent') {
|
|
||||||
this.manifestInfoTitle = 'REPOSITORY.COPY_PARENT_ID';
|
|
||||||
this.tagID = tag.parent;
|
|
||||||
}
|
|
||||||
this.showTagManifestOpened = true;
|
this.showTagManifestOpened = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
export class TagView {
|
|
||||||
tag: string;
|
|
||||||
pullCommand: string;
|
|
||||||
signed: number = -1;
|
|
||||||
author: string;
|
|
||||||
created: Date;
|
|
||||||
dockerVersion: string;
|
|
||||||
architecture: string;
|
|
||||||
os: string;
|
|
||||||
id: string;
|
|
||||||
parent: string;
|
|
||||||
}
|
|
@ -11,30 +11,13 @@
|
|||||||
// 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.
|
||||||
/*
|
|
||||||
{
|
|
||||||
"tag": "latest",
|
|
||||||
"manifest": {
|
|
||||||
"schemaVersion": 1,
|
|
||||||
"name": "library/photon",
|
|
||||||
"tag": "latest",
|
|
||||||
"architecture": "amd64",
|
|
||||||
"history": []
|
|
||||||
},
|
|
||||||
|
|
||||||
*/
|
|
||||||
export class Tag {
|
export class Tag {
|
||||||
tag: string;
|
digest: string;
|
||||||
manifest: {
|
name: string;
|
||||||
schemaVersion: number;
|
architecture: string;
|
||||||
name: string;
|
os: string;
|
||||||
tag: string;
|
docker_version: string;
|
||||||
architecture: string;
|
author: string;
|
||||||
history: [
|
created: Date;
|
||||||
{
|
signature?: {[key: string]: any | any[]}
|
||||||
v1Compatibility: string;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
signed: number;
|
|
||||||
}
|
}
|
@ -1,30 +0,0 @@
|
|||||||
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
/*
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"tag": "2.0",
|
|
||||||
"hashes": {
|
|
||||||
"sha256": "E1lggRW5RZnlZBY4usWu8d36p5u5YFfr9B68jTOs+Kc="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class VerifiedSignature {
|
|
||||||
tag: string;
|
|
||||||
hashes: {
|
|
||||||
sha256: string;
|
|
||||||
}
|
|
||||||
}
|
|
@ -300,8 +300,7 @@
|
|||||||
"FAILED_TO_DELETE_TARGET_IN_USED": "Failed to delete the endpoint in use."
|
"FAILED_TO_DELETE_TARGET_IN_USED": "Failed to delete the endpoint in use."
|
||||||
},
|
},
|
||||||
"REPOSITORY": {
|
"REPOSITORY": {
|
||||||
"COPY_ID": "Copy ID",
|
"COPY_DIGEST_ID": "Copy Digest ID",
|
||||||
"COPY_PARENT_ID": "Copy Parent ID",
|
|
||||||
"DELETE": "Delete",
|
"DELETE": "Delete",
|
||||||
"NAME": "Name",
|
"NAME": "Name",
|
||||||
"TAGS_COUNT": "Tags",
|
"TAGS_COUNT": "Tags",
|
||||||
|
@ -300,8 +300,7 @@
|
|||||||
"FAILED_TO_DELETE_TARGET_IN_USED": "无法删除正在使用的目标。"
|
"FAILED_TO_DELETE_TARGET_IN_USED": "无法删除正在使用的目标。"
|
||||||
},
|
},
|
||||||
"REPOSITORY": {
|
"REPOSITORY": {
|
||||||
"COPY_ID": "复制ID",
|
"COPY_DIGEST_ID": "复制摘要ID",
|
||||||
"COPY_PARENT_ID": "复制父级ID",
|
|
||||||
"DELETE": "删除",
|
"DELETE": "删除",
|
||||||
"NAME": "名称",
|
"NAME": "名称",
|
||||||
"TAGS_COUNT": "标签数",
|
"TAGS_COUNT": "标签数",
|
||||||
|
Loading…
Reference in New Issue
Block a user