Merge pull request #2551 from ywk253100/170616_tag

Add get tag API
This commit is contained in:
Wenkai Yin 2017-06-18 11:10:33 +08:00 committed by GitHub
commit 6e89f11ffc
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/statistics", &StatisticAPI{})
beego.Router("/api/users/?:id", &UserAPI{}) beego.Router("/api/users/?:id", &UserAPI{})
beego.Router("/api/logs", &LogAPI{}) 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", &RepositoryAPI{}, "get:GetTags")
beego.Router("/api/repositories/*/tags/:tag/manifest", &RepositoryAPI{}, "get:GetManifests") beego.Router("/api/repositories/*/tags/:tag/manifest", &RepositoryAPI{}, "get:GetManifests")
beego.Router("/api/repositories/*/signatures", &RepositoryAPI{}, "get:GetSignatures") 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 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 //Get tags of a relevant repository
func (a testapi) GetReposTags(authInfo usrInfo, repoName string) (int, interface{}, error) { func (a testapi) GetReposTags(authInfo usrInfo, repoName string) (int, interface{}, error) {
_sling := sling.New().Get(a.basePath) _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 // GetTags returns tags of a repository
func (ra *RepositoryAPI) GetTags() { func (ra *RepositoryAPI) GetTags() {
repoName := ra.GetString(":splat") repoName := ra.GetString(":splat")
@ -324,65 +384,74 @@ func (ra *RepositoryAPI) GetTags() {
ra.CustomAbort(http.StatusInternalServerError, "internal error") ra.CustomAbort(http.StatusInternalServerError, "internal error")
} }
// get tags tags, err := getSimpleTags(client)
tags, err := getDetailedTags(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 { if err != nil {
regErr, ok := err.(*registry_error.Error) regErr, ok := err.(*registry_error.Error)
if !ok { if !ok {
ra.HandleInternalServerError(fmt.Sprintf( ra.HandleInternalServerError(fmt.Sprintf("failed to get tag of %s: %v", repoName, err))
"failed to list tags of repository %s: %v", repoName, err))
return return
} }
ra.RenderError(regErr.StatusCode, regErr.Detail) ra.RenderError(regErr.StatusCode, regErr.Detail)
return 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 // get signatures
signatures := map[string]*notary.Target{} signatures := map[string]*notary.Target{}
if config.WithNotary() { if config.WithNotary() {
signatures, err = getSignatures(repoName, ra.SecurityCtx.GetUsername()) signatures, err = getSignatures(repository, username)
if err != nil { if err != nil {
ra.HandleInternalServerError(fmt.Sprintf( return nil, err
"failed to get signatures of repository %s: %v", repoName, err))
return
} }
} }
// assemble the response // assemble the response
tagResps := []*tagResp{} result := []*tagResp{}
for _, tag := range tags { for _, tag := range list {
tagResp := &tagResp{ item := &tagResp{
tag: *tag, tag: *tag,
} }
//TODO: only when deployed with Clair... if config.WithClair() {
tagResp.ScanOverview = getScanOverview(tag.Digest, tag.Name) item.ScanOverview = getScanOverview(item.Digest, item.Name)
}
// compare both digest and tag // compare both digest and tag
if signature, ok := signatures[tag.Digest]; ok { if signature, ok := signatures[item.Digest]; ok {
if tag.Name == signature.Tag { if item.Name == signature.Tag {
tagResp.Signature = signature item.Signature = signature
} }
} }
result = append(result, item)
}
tagResps = append(tagResps, tagResp) return result, nil
}
ra.Data["json"] = tagResps
ra.ServeJSON()
} }
// get tags of the repository, read manifest for every tag // get tags of the repository, read manifest for every tag
// and assemble necessary attrs(os, architecture, etc.) into // and assemble necessary attrs(os, architecture, etc.) into
// one struct // one struct
func getDetailedTags(client *registry.Repository) ([]*tag, error) { func getDetailedTags(client *registry.Repository, tags []string) ([]*tag, error) {
ts, err := getSimpleTags(client)
if err != nil {
return nil, err
}
list := []*tag{} list := []*tag{}
for _, t := range tags {
for _, t := range ts {
// the ignored manifest can be used to calculate the image size // the ignored manifest can be used to calculate the image size
digest, _, config, err := getV2Manifest(client, t) digest, _, config, err := getV2Manifest(client, t)
if err != nil { if err != nil {
@ -610,6 +679,29 @@ func (ra *RepositoryAPI) GetTopRepos() {
//GetSignatures returns signatures of a repository //GetSignatures returns signatures of a repository
func (ra *RepositoryAPI) GetSignatures() { func (ra *RepositoryAPI) GetSignatures() {
repoName := ra.GetString(":splat") 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(), targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
ra.SecurityCtx.GetUsername(), repoName) ra.SecurityCtx.GetUsername(), repoName)
if err != nil { if err != nil {

View File

@ -15,6 +15,7 @@ package api
import ( import (
"fmt" "fmt"
"net/http"
"testing" "testing"
"github.com/stretchr/testify/assert" "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") fmt.Printf("\n")
} }

View File

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