mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-23 16:11:24 +01:00
Merge pull request #2269 from steven-zou/master
Implement tag service interface
This commit is contained in:
commit
b354653735
@ -11,6 +11,30 @@ export interface Base {
|
||||
update_time?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for tag history
|
||||
*
|
||||
* @export
|
||||
* @interface TagCompatibility
|
||||
*/
|
||||
export interface TagCompatibility {
|
||||
v1Compatibility: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for tag manifest
|
||||
*
|
||||
* @export
|
||||
* @interface TagManifest
|
||||
*/
|
||||
export interface TagManifest {
|
||||
schemaVersion: number;
|
||||
name: string;
|
||||
tag: string;
|
||||
architecture: string;
|
||||
history: TagCompatibility[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for Repository
|
||||
*
|
||||
@ -35,7 +59,11 @@ export interface Repository extends Base {
|
||||
* @interface Tag
|
||||
* @extends {Base}
|
||||
*/
|
||||
export interface Tag extends Base { }
|
||||
export interface Tag extends Base {
|
||||
tag: string;
|
||||
manifest: TagManifest;
|
||||
signed?: number; //May NOT exist
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for registry endpoints.
|
||||
@ -69,14 +97,14 @@ export interface ReplicationJob { }
|
||||
* @interface AccessLog
|
||||
*/
|
||||
export interface AccessLog {
|
||||
log_id: number,
|
||||
project_id: number,
|
||||
repo_name: string,
|
||||
repo_tag: string,
|
||||
operation: string,
|
||||
op_time: string | Date,
|
||||
user_id: number,
|
||||
username: string,
|
||||
keywords?: string, //NOT used now
|
||||
guid?: string //NOT used now
|
||||
log_id: number;
|
||||
project_id: number;
|
||||
repo_name: string;
|
||||
repo_tag: string;
|
||||
operation: string;
|
||||
op_time: string | Date;
|
||||
user_id: number;
|
||||
username: string;
|
||||
keywords?: string; //NOT used now
|
||||
guid?: string; //NOT used now
|
||||
}
|
@ -1,15 +1,62 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
import { TestBed, inject, async } from '@angular/core/testing';
|
||||
|
||||
import { TagService, TagDefaultService } from './tag.service';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
|
||||
import { Tag, TagCompatibility, TagManifest } from './interface';
|
||||
|
||||
import { VerifiedSignature } from './tag.service';
|
||||
import { toPromise } from '../utils';
|
||||
|
||||
describe('TagService', () => {
|
||||
let mockComp: TagCompatibility[] = [{
|
||||
v1Compatibility: '{"architecture":"amd64","author":"NGINX Docker Maintainers \\"docker-maint@nginx.com\\"","config":{"Hostname":"6b3797ab1e90","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"443/tcp":{},"80/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.5-1~jessie"],"Cmd":["nginx","-g","daemon off;"],"ArgsEscaped":true,"Image":"sha256:47a33f0928217b307cf9f20920a0c6445b34ae974a60c1b4fe73b809379ad928","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":[],"Labels":{}},"container":"f1883a3fb44b0756a2a3b1e990736a44b1387183125351370042ce7bd9ffc338","container_config":{"Hostname":"6b3797ab1e90","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"443/tcp":{},"80/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.5-1~jessie"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\\"nginx\\" \\"-g\\" \\"daemon off;\\"]"],"ArgsEscaped":true,"Image":"sha256:47a33f0928217b307cf9f20920a0c6445b34ae974a60c1b4fe73b809379ad928","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":[],"Labels":{}},"created":"2016-11-08T22:41:15.912313785Z","docker_version":"1.12.3","id":"db3700426e6d7c1402667f42917109b2467dd49daa85d38ac99854449edc20b3","os":"linux","parent":"f3ef5f96caf99a18c6821487102c136b00e0275b1da0c7558d7090351f9d447e","throwaway":true}'
|
||||
}];
|
||||
let mockManifest: TagManifest = {
|
||||
schemaVersion: 1,
|
||||
name: 'library/nginx',
|
||||
tag: '1.11.5',
|
||||
architecture: 'amd64',
|
||||
history: mockComp
|
||||
};
|
||||
|
||||
let mockTags: Tag[] = [{
|
||||
tag: '1.11.5',
|
||||
manifest: mockManifest
|
||||
}];
|
||||
|
||||
let mockSignatures: VerifiedSignature[] = [{
|
||||
tag: '1.11.5',
|
||||
hashes: {
|
||||
sha256: 'fake'
|
||||
}
|
||||
}];
|
||||
|
||||
let mockSignatures2: VerifiedSignature[] = [{
|
||||
tag: '1.11.15',
|
||||
hashes: {
|
||||
sha256: 'fake2'
|
||||
}
|
||||
}];
|
||||
|
||||
beforeEach(() => {
|
||||
const mockConfig: IServiceConfig = {
|
||||
repositoryBaseEndpoint: "/api/repositories/testing"
|
||||
};
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
SharedModule
|
||||
],
|
||||
providers: [
|
||||
TagDefaultService,
|
||||
{
|
||||
provide: TagService,
|
||||
useClass: TagDefaultService
|
||||
}, {
|
||||
provide: SERVICE_CONFIG,
|
||||
useValue: mockConfig
|
||||
}]
|
||||
});
|
||||
});
|
||||
@ -17,4 +64,51 @@ describe('TagService', () => {
|
||||
it('should be initialized', inject([TagDefaultService], (service: TagService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
|
||||
it('should get tags with signed status[1] if signatures exists', async(inject([TagDefaultService], (service: TagService) => {
|
||||
expect(service).toBeTruthy();
|
||||
let spy1: jasmine.Spy = spyOn(service, '_getTags')
|
||||
.and.returnValue(Promise.resolve(mockTags));
|
||||
let spy2: jasmine.Spy = spyOn(service, '_getSignatures')
|
||||
.and.returnValue(Promise.resolve(mockSignatures));
|
||||
|
||||
toPromise<Tag[]>(service.getTags('library/nginx'))
|
||||
.then(tags => {
|
||||
expect(tags).toBeTruthy();
|
||||
expect(tags.length).toBe(1);
|
||||
expect(tags[0].signed).toBe(1);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should get tags with not-signed status[0] if signatures exists', async(inject([TagDefaultService], (service: TagService) => {
|
||||
expect(service).toBeTruthy();
|
||||
let spy1: jasmine.Spy = spyOn(service, '_getTags')
|
||||
.and.returnValue(Promise.resolve(mockTags));
|
||||
let spy2: jasmine.Spy = spyOn(service, '_getSignatures')
|
||||
.and.returnValue(Promise.resolve(mockSignatures2));
|
||||
|
||||
toPromise<Tag[]>(service.getTags('library/nginx'))
|
||||
.then(tags => {
|
||||
expect(tags).toBeTruthy();
|
||||
expect(tags.length).toBe(1);
|
||||
expect(tags[0].signed).toBe(0);
|
||||
});
|
||||
})));
|
||||
|
||||
it('should get tags with default signed status[-1] if signatures not exist', async(inject([TagDefaultService], (service: TagService) => {
|
||||
expect(service).toBeTruthy();
|
||||
let spy1: jasmine.Spy = spyOn(service, '_getTags')
|
||||
.and.returnValue(Promise.resolve(mockTags));
|
||||
let spy2: jasmine.Spy = spyOn(service, '_getSignatures')
|
||||
.and.returnValue(Promise.reject("Error"));
|
||||
|
||||
toPromise<Tag[]>(service.getTags('library/nginx'))
|
||||
.then(tags => {
|
||||
expect(tags).toBeTruthy();
|
||||
expect(tags.length).toBe(1);
|
||||
expect(tags[0].signed).toBe(-1);
|
||||
});
|
||||
})));
|
||||
|
||||
|
||||
});
|
||||
|
@ -1,8 +1,25 @@
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { RequestQueryParams } from './RequestQueryParams';
|
||||
import { Tag } from './interface';
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Injectable, Inject } from "@angular/core";
|
||||
import 'rxjs/add/observable/of';
|
||||
import { Http } from '@angular/http';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { buildHttpRequestOptions, HTTP_JSON_OPTIONS } from '../utils';
|
||||
|
||||
/**
|
||||
* For getting tag signatures.
|
||||
* This is temporary, will be removed in future.
|
||||
*
|
||||
* @export
|
||||
* @class VerifiedSignature
|
||||
*/
|
||||
export class VerifiedSignature {
|
||||
tag: string;
|
||||
hashes: {
|
||||
sha256: string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the service methods to handle the repository tag related things.
|
||||
@ -19,11 +36,11 @@ export abstract class TagService {
|
||||
* @abstract
|
||||
* @param {string} repositoryName
|
||||
* @param {RequestQueryParams} [queryParams]
|
||||
* @returns {(Observable<Tag[]> | Tag[])}
|
||||
* @returns {(Observable<Tag[]> | Promise<Tag[]> | Tag[])}
|
||||
*
|
||||
* @memberOf TagService
|
||||
*/
|
||||
abstract getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable<Tag[]> | Tag[];
|
||||
abstract getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable<Tag[]> | Promise<Tag[]> | Tag[];
|
||||
|
||||
/**
|
||||
* Delete the specified tag.
|
||||
@ -35,7 +52,7 @@ export abstract class TagService {
|
||||
*
|
||||
* @memberOf TagService
|
||||
*/
|
||||
abstract deleteTag(repositoryName: string, tag: string): Observable<any> | any;
|
||||
abstract deleteTag(repositoryName: string, tag: string): Observable<any> | Promise<Tag> | any;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,11 +64,73 @@ export abstract class TagService {
|
||||
*/
|
||||
@Injectable()
|
||||
export class TagDefaultService extends TagService {
|
||||
public getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable<Tag[]> | Tag[] {
|
||||
return Observable.of([]);
|
||||
_baseUrl: string;
|
||||
|
||||
constructor(
|
||||
private http: Http,
|
||||
@Inject(SERVICE_CONFIG) private config: IServiceConfig
|
||||
) {
|
||||
super();
|
||||
this._baseUrl = this.config.repositoryBaseEndpoint ? this.config.repositoryBaseEndpoint : '/api/repositories';
|
||||
}
|
||||
|
||||
public deleteTag(repositoryName: string, tag: string): Observable<any> | any {
|
||||
return Observable.of({});
|
||||
//Private methods
|
||||
//These two methods are temporary, will be deleted in future after API refactored
|
||||
_getTags(repositoryName: string, queryParams?: RequestQueryParams): Promise<Tag[]> {
|
||||
if (!queryParams) {
|
||||
queryParams = new RequestQueryParams();
|
||||
}
|
||||
|
||||
queryParams.set('detail', '1');
|
||||
let url: string = `${this._baseUrl}/${repositoryName}/tags`;
|
||||
|
||||
return this.http.get(url, buildHttpRequestOptions(queryParams)).toPromise()
|
||||
.then(response => response.json() as Tag[])
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
|
||||
_getSignatures(repositoryName: string): Promise<VerifiedSignature[]> {
|
||||
let url: string = `${this._baseUrl}/${repositoryName}/signatures`;
|
||||
return this.http.get(url, HTTP_JSON_OPTIONS).toPromise()
|
||||
.then(response => response.json() as VerifiedSignature[])
|
||||
.catch(error => Promise.reject(error))
|
||||
}
|
||||
|
||||
public getTags(repositoryName: string, queryParams?: RequestQueryParams): Observable<Tag[]> | Promise<Tag[]> | Tag[] {
|
||||
if (!repositoryName) {
|
||||
return Promise.reject("Bad argument");
|
||||
}
|
||||
|
||||
return this._getTags(repositoryName, queryParams)
|
||||
.then(tags => {
|
||||
return this._getSignatures(repositoryName)
|
||||
.then(signatures => {
|
||||
tags.forEach(tag => {
|
||||
let foundOne: VerifiedSignature | undefined = signatures.find(signature => signature.tag === tag.tag);
|
||||
if (foundOne) {
|
||||
tag.signed = 1;//Signed
|
||||
} else {
|
||||
tag.signed = 0;//Not signed
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
})
|
||||
.catch(error => {
|
||||
tags.forEach(tag => tag.signed = -1);//No signature info
|
||||
return tags;
|
||||
})
|
||||
})
|
||||
.catch(error => Promise.reject(error))
|
||||
}
|
||||
|
||||
public deleteTag(repositoryName: string, tag: string): Observable<any> | Promise<Tag> | any {
|
||||
if (!repositoryName || !tag) {
|
||||
return Promise.reject("Bad argument");
|
||||
}
|
||||
|
||||
let url: string = `${this._baseUrl}/${repositoryName}/tags/${tag}`;
|
||||
return this.http.delete(url, HTTP_JSON_OPTIONS).toPromise()
|
||||
.then(response => response)
|
||||
.catch(error => Promise.reject(error));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user