add get tag API

This commit is contained in:
Wenkai Yin 2017-06-16 17:21:53 +08:00
parent 9fb0ad6c0d
commit ea5de0adf2
4 changed files with 161 additions and 32 deletions

View File

@ -103,7 +103,7 @@ func init() {
beego.Router("/api/statistics", &StatisticAPI{})
beego.Router("/api/users/?:id", &UserAPI{})
beego.Router("/api/logs", &LogAPI{})
beego.Router("/api/repositories/*/tags/?:tag", &RepositoryAPI{}, "delete:Delete")
beego.Router("/api/repositories/*/tags/:tag", &RepositoryAPI{}, "delete:Delete;get:GetTag")
beego.Router("/api/repositories/*/tags", &RepositoryAPI{}, "get:GetTags")
beego.Router("/api/repositories/*/tags/:tag/manifest", &RepositoryAPI{}, "get:GetManifests")
beego.Router("/api/repositories/*/signatures", &RepositoryAPI{}, "get:GetSignatures")
@ -484,6 +484,25 @@ func (a testapi) GetRepos(authInfo usrInfo, projectID, keyword string) (
return code, nil, nil
}
func (a testapi) GetTag(authInfo usrInfo, repository string, tag string) (int, *tagResp, error) {
_sling := sling.New().Get(a.basePath).Path(fmt.Sprintf("/api/repositories/%s/tags/%s", repository, tag))
code, data, err := request(_sling, jsonAcceptHeader, authInfo)
if err != nil {
return 0, nil, err
}
if code != http.StatusOK {
log.Printf("failed to get tag of %s:%s: %d %s \n", repository, tag, code, string(data))
return code, nil, nil
}
result := tagResp{}
if err := json.Unmarshal(data, &result); err != nil {
return 0, nil, err
}
return http.StatusOK, &result, nil
}
//Get tags of a relevant repository
func (a testapi) GetReposTags(authInfo usrInfo, repoName string) (int, interface{}, error) {
_sling := sling.New().Get(a.basePath)

View File

@ -292,6 +292,66 @@ func (ra *RepositoryAPI) Delete() {
}
}
// GetTag returns the tag of a repository
func (ra *RepositoryAPI) GetTag() {
repository := ra.GetString(":splat")
project, _ := utils.ParseRepository(repository)
exist, err := ra.ProjectMgr.Exist(project)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
project, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", project))
return
}
if !ra.SecurityCtx.HasReadPerm(project) {
if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized()
return
}
ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return
}
client, err := ra.initRepositoryClient(repository)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to initialize the client for %s: %v",
repository, err))
return
}
tag := ra.GetString(":tag")
_, exist, err = client.ManifestExist(tag)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of %s:%s: %v", repository, tag, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("%s not found", tag))
return
}
result, err := assemble(client, repository, []string{tag},
ra.SecurityCtx.GetUsername())
if err != nil {
regErr, ok := err.(*registry_error.Error)
if !ok {
ra.HandleInternalServerError(fmt.Sprintf("failed to get tag %s of %s: %v", tag, repository, err))
return
}
ra.RenderError(regErr.StatusCode, regErr.Detail)
return
}
ra.Data["json"] = result[0]
ra.ServeJSON()
}
// GetTags returns tags of a repository
func (ra *RepositoryAPI) GetTags() {
repoName := ra.GetString(":splat")
@ -324,65 +384,74 @@ func (ra *RepositoryAPI) GetTags() {
ra.CustomAbort(http.StatusInternalServerError, "internal error")
}
// get tags
tags, err := getDetailedTags(client)
tags, err := getSimpleTags(client)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to get tag of %s: %v", repoName, err))
return
}
result, err := assemble(client, repoName, tags, ra.SecurityCtx.GetUsername())
if err != nil {
regErr, ok := err.(*registry_error.Error)
if !ok {
ra.HandleInternalServerError(fmt.Sprintf(
"failed to list tags of repository %s: %v", repoName, err))
ra.HandleInternalServerError(fmt.Sprintf("failed to get tag of %s: %v", repoName, err))
return
}
ra.RenderError(regErr.StatusCode, regErr.Detail)
return
}
ra.Data["json"] = result
ra.ServeJSON()
}
// get config, signature and scan overview and assemble them into one
// struct for each tag in tags
func assemble(client *registry.Repository, repository string,
tags []string, username string) ([]*tagResp, error) {
// get configs
list, err := getDetailedTags(client, tags)
if err != nil {
return nil, err
}
// get signatures
signatures := map[string]*notary.Target{}
if config.WithNotary() {
signatures, err = getSignatures(repoName, ra.SecurityCtx.GetUsername())
signatures, err = getSignatures(repository, username)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf(
"failed to get signatures of repository %s: %v", repoName, err))
return
return nil, err
}
}
// assemble the response
tagResps := []*tagResp{}
for _, tag := range tags {
tagResp := &tagResp{
result := []*tagResp{}
for _, tag := range list {
item := &tagResp{
tag: *tag,
}
//TODO: only when deployed with Clair...
tagResp.ScanOverview = getScanOverview(tag.Digest, tag.Name)
// compare both digest and tag
if signature, ok := signatures[tag.Digest]; ok {
if tag.Name == signature.Tag {
tagResp.Signature = signature
}
if config.WithClair() {
item.ScanOverview = getScanOverview(item.Digest, item.Name)
}
tagResps = append(tagResps, tagResp)
// compare both digest and tag
if signature, ok := signatures[item.Digest]; ok {
if item.Name == signature.Tag {
item.Signature = signature
}
}
result = append(result, item)
}
ra.Data["json"] = tagResps
ra.ServeJSON()
return result, nil
}
// get tags of the repository, read manifest for every tag
// and assemble necessary attrs(os, architecture, etc.) into
// one struct
func getDetailedTags(client *registry.Repository) ([]*tag, error) {
ts, err := getSimpleTags(client)
if err != nil {
return nil, err
}
func getDetailedTags(client *registry.Repository, tags []string) ([]*tag, error) {
list := []*tag{}
for _, t := range ts {
for _, t := range tags {
// the ignored manifest can be used to calculate the image size
digest, _, config, err := getV2Manifest(client, t)
if err != nil {
@ -610,6 +679,29 @@ func (ra *RepositoryAPI) GetTopRepos() {
//GetSignatures returns signatures of a repository
func (ra *RepositoryAPI) GetSignatures() {
repoName := ra.GetString(":splat")
projectName, _ := utils.ParseRepository(repoName)
exist, err := ra.ProjectMgr.Exist(projectName)
if err != nil {
ra.HandleInternalServerError(fmt.Sprintf("failed to check the existence of project %s: %v",
projectName, err))
return
}
if !exist {
ra.HandleNotFound(fmt.Sprintf("project %s not found", projectName))
return
}
if !ra.SecurityCtx.HasReadPerm(projectName) {
if !ra.SecurityCtx.IsAuthenticated() {
ra.HandleUnauthorized()
return
}
ra.HandleForbidden(ra.SecurityCtx.GetUsername())
return
}
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
ra.SecurityCtx.GetUsername(), repoName)
if err != nil {

View File

@ -15,6 +15,7 @@ package api
import (
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
@ -98,6 +99,23 @@ func TestGetReposTags(t *testing.T) {
}
}
//-------------------case 3 : response code = 404------------------------//
fmt.Println("case 3 : response code = 404")
repository = "library/hello-world"
tag := "not_exist_tag"
code, result, err := apiTest.GetTag(*admin, repository, tag)
assert.Nil(err)
assert.Equal(http.StatusNotFound, code)
//-------------------case 4 : response code = 200------------------------//
fmt.Println("case 4 : response code = 200")
repository = "library/hello-world"
tag = "latest"
code, result, err = apiTest.GetTag(*admin, repository, tag)
assert.Nil(err)
assert.Equal(http.StatusOK, code)
assert.Equal(tag, result.Name)
fmt.Printf("\n")
}

View File

@ -74,7 +74,7 @@ func initRouters() {
beego.Router("/api/internal/syncregistry", &api.InternalAPI{}, "post:SyncRegistry")
beego.Router("/api/repositories", &api.RepositoryAPI{}, "get:Get")
beego.Router("/api/repositories/*", &api.RepositoryAPI{}, "delete:Delete")
beego.Router("/api/repositories/*/tags/:tag", &api.RepositoryAPI{}, "delete:Delete")
beego.Router("/api/repositories/*/tags/:tag", &api.RepositoryAPI{}, "delete:Delete;get:GetTag")
beego.Router("/api/repositories/*/tags", &api.RepositoryAPI{}, "get:GetTags")
beego.Router("/api/repositories/*/tags/:tag/scan", &api.RepositoryAPI{}, "post:ScanImage")
beego.Router("/api/repositories/*/tags/:tag/manifest", &api.RepositoryAPI{}, "get:GetManifests")