Merge pull request #8340 from ywk253100/190719_dependency

Fix package import cycle issue
This commit is contained in:
Steven Zou 2019-07-19 15:58:23 +08:00 committed by GitHub
commit b2b2fea970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 118 additions and 81 deletions

View File

@ -16,6 +16,9 @@ package models
import ( import (
"time" "time"
"github.com/goharbor/harbor/src/common/utils/notary/model"
"github.com/theupdateframework/notary/tuf/data"
) )
// RepoTable is the table name for repository // RepoTable is the table name for repository
@ -47,3 +50,36 @@ type RepositoryQuery struct {
Pagination Pagination
Sorting Sorting
} }
// TagResp holds the information of one image tag
type TagResp struct {
TagDetail
Signature *model.Target `json:"signature"`
ScanOverview *ImgScanOverview `json:"scan_overview,omitempty"`
Labels []*Label `json:"labels"`
}
// TagDetail ...
type TagDetail struct {
Digest string `json:"digest"`
Name string `json:"name"`
Size int64 `json:"size"`
Architecture string `json:"architecture"`
OS string `json:"os"`
OSVersion string `json:"os.version"`
DockerVersion string `json:"docker_version"`
Author string `json:"author"`
Created time.Time `json:"created"`
Config *TagCfg `json:"config"`
}
// TagCfg ...
type TagCfg struct {
Labels map[string]string `json:"labels"`
}
// Signature ...
type Signature struct {
Tag string `json:"tag"`
Hashes data.Hashes `json:"hashes"`
}

View File

@ -22,6 +22,8 @@ import (
"path" "path"
"strings" "strings"
"github.com/goharbor/harbor/src/common/utils/notary/model"
"github.com/docker/distribution/registry/auth/token" "github.com/docker/distribution/registry/auth/token"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/common/utils/registry" "github.com/goharbor/harbor/src/common/utils/registry"
@ -41,14 +43,6 @@ var (
mockRetriever notary.PassRetriever mockRetriever notary.PassRetriever
) )
// Target represents the json object of a target of a docker image in notary.
// The struct will be used when repository is know so it won'g contain the name of a repository.
type Target struct {
Tag string `json:"tag"`
Hashes data.Hashes `json:"hashes"`
// TODO: update fields as needed.
}
func init() { func init() {
mockRetriever = func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) { mockRetriever = func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
passphrase = "hardcode" passphrase = "hardcode"
@ -60,7 +54,7 @@ func init() {
} }
// GetInternalTargets wraps GetTargets to read config values for getting full-qualified repo from internal notary instance. // GetInternalTargets wraps GetTargets to read config values for getting full-qualified repo from internal notary instance.
func GetInternalTargets(notaryEndpoint string, username string, repo string) ([]Target, error) { func GetInternalTargets(notaryEndpoint string, username string, repo string) ([]model.Target, error) {
ext, err := config.ExtEndpoint() ext, err := config.ExtEndpoint()
if err != nil { if err != nil {
log.Errorf("Error while reading external endpoint: %v", err) log.Errorf("Error while reading external endpoint: %v", err)
@ -74,8 +68,8 @@ func GetInternalTargets(notaryEndpoint string, username string, repo string) ([]
// GetTargets is a help function called by API to fetch signature information of a given repository. // GetTargets is a help function called by API to fetch signature information of a given repository.
// Per docker's convention the repository should contain the information of endpoint, i.e. it should look // Per docker's convention the repository should contain the information of endpoint, i.e. it should look
// like "192.168.0.1/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo) // like "192.168.0.1/library/ubuntu", instead of "library/ubuntu" (fqRepo for fully-qualified repo)
func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]Target, error) { func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]model.Target, error) {
res := []Target{} res := []model.Target{}
t, err := tokenutil.MakeToken(username, tokenutil.Notary, t, err := tokenutil.MakeToken(username, tokenutil.Notary,
[]*token.ResourceActions{ []*token.ResourceActions{
{ {
@ -109,13 +103,16 @@ func GetTargets(notaryEndpoint string, username string, fqRepo string) ([]Target
log.Warningf("Failed to clear cached root.json: %s, error: %v, when repo is removed from notary the signature status maybe incorrect", rootJSON, rmErr) log.Warningf("Failed to clear cached root.json: %s, error: %v, when repo is removed from notary the signature status maybe incorrect", rootJSON, rmErr)
} }
for _, t := range targets { for _, t := range targets {
res = append(res, Target{t.Name, t.Hashes}) res = append(res, model.Target{
Tag: t.Name,
Hashes: t.Hashes,
})
} }
return res, nil return res, nil
} }
// DigestFromTarget get a target and return the value of digest, in accordance to Docker-Content-Digest // DigestFromTarget get a target and return the value of digest, in accordance to Docker-Content-Digest
func DigestFromTarget(t Target) (string, error) { func DigestFromTarget(t model.Target) (string, error) {
sha, ok := t.Hashes["sha256"] sha, ok := t.Hashes["sha256"]
if !ok { if !ok {
return "", fmt.Errorf("no valid hash, expecting sha256") return "", fmt.Errorf("no valid hash, expecting sha256")

View File

@ -17,6 +17,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/goharbor/harbor/src/common/utils/notary/model"
notarytest "github.com/goharbor/harbor/src/common/utils/notary/test" notarytest "github.com/goharbor/harbor/src/common/utils/notary/test"
"github.com/goharbor/harbor/src/common/utils/test" "github.com/goharbor/harbor/src/common/utils/test"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
@ -81,17 +83,19 @@ func TestGetDigestFromTarget(t *testing.T) {
} }
}` }`
var t1 Target var t1 model.Target
err := json.Unmarshal([]byte(str), &t1) err := json.Unmarshal([]byte(str), &t1)
if err != nil { if err != nil {
panic(err) panic(err)
} }
hash2 := make(map[string][]byte) hash2 := make(map[string][]byte)
t2 := Target{"2.0", hash2} t2 := model.Target{
Tag: "2.0",
Hashes: hash2,
}
d1, err1 := DigestFromTarget(t1) d1, err1 := DigestFromTarget(t1)
assert.Nil(t, err1, "Unexpected error: %v", err1) assert.Nil(t, err1, "Unexpected error: %v", err1)
assert.Equal(t, "sha256:1359608115b94599e5641638bac5aef1ddfaa79bb96057ebf41ebc8d33acf8a7", d1, "digest mismatch") assert.Equal(t, "sha256:1359608115b94599e5641638bac5aef1ddfaa79bb96057ebf41ebc8d33acf8a7", d1, "digest mismatch")
_, err2 := DigestFromTarget(t2) _, err2 := DigestFromTarget(t2)
assert.NotNil(t, err2, "") assert.NotNil(t, err2, "")
} }

View File

@ -0,0 +1,25 @@
// Copyright Project Harbor Authors
//
// 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.
package model
import "github.com/theupdateframework/notary/tuf/data"
// Target represents the json object of a target of a docker image in notary.
// The struct will be used when repository is know so it won'g contain the name of a repository.
type Target struct {
Tag string `json:"tag"`
Hashes data.Hashes `json:"hashes"`
// TODO: update fields as needed.
}

View File

@ -555,7 +555,7 @@ 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) { func (a testapi) GetTag(authInfo usrInfo, repository string, tag string) (int, *models.TagResp, error) {
_sling := sling.New().Get(a.basePath).Path(fmt.Sprintf("/api/repositories/%s/tags/%s", repository, tag)) _sling := sling.New().Get(a.basePath).Path(fmt.Sprintf("/api/repositories/%s/tags/%s", repository, tag))
code, data, err := request(_sling, jsonAcceptHeader, authInfo) code, data, err := request(_sling, jsonAcceptHeader, authInfo)
if err != nil { if err != nil {
@ -567,7 +567,7 @@ func (a testapi) GetTag(authInfo usrInfo, repository string, tag string) (int, *
return code, nil, nil return code, nil, nil
} }
result := TagResp{} result := models.TagResp{}
if err := json.Unmarshal(data, &result); err != nil { if err := json.Unmarshal(data, &result); err != nil {
return 0, nil, err return 0, nil, err
} }
@ -591,7 +591,7 @@ func (a testapi) GetReposTags(authInfo usrInfo, repoName string) (int, interface
return httpStatusCode, body, nil return httpStatusCode, body, nil
} }
result := []TagResp{} result := []models.TagResp{}
if err := json.Unmarshal(body, &result); err != nil { if err := json.Unmarshal(body, &result); err != nil {
return 0, nil, err return 0, nil, err
} }

View File

@ -16,6 +16,7 @@ package api
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -24,10 +25,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/goharbor/harbor/src/pkg/scan"
"errors"
"github.com/docker/distribution/manifest/schema1" "github.com/docker/distribution/manifest/schema1"
"github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/manifest/schema2"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
@ -38,9 +35,11 @@ import (
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/common/utils/log" "github.com/goharbor/harbor/src/common/utils/log"
"github.com/goharbor/harbor/src/common/utils/notary" "github.com/goharbor/harbor/src/common/utils/notary"
notarymodel "github.com/goharbor/harbor/src/common/utils/notary/model"
"github.com/goharbor/harbor/src/common/utils/registry" "github.com/goharbor/harbor/src/common/utils/registry"
"github.com/goharbor/harbor/src/core/config" "github.com/goharbor/harbor/src/core/config"
coreutils "github.com/goharbor/harbor/src/core/utils" coreutils "github.com/goharbor/harbor/src/core/utils"
"github.com/goharbor/harbor/src/pkg/scan"
"github.com/goharbor/harbor/src/replication" "github.com/goharbor/harbor/src/replication"
"github.com/goharbor/harbor/src/replication/event" "github.com/goharbor/harbor/src/replication/event"
"github.com/goharbor/harbor/src/replication/model" "github.com/goharbor/harbor/src/replication/model"
@ -80,31 +79,6 @@ func (r reposSorter) Less(i, j int) bool {
return r[i].Index < r[j].Index return r[i].Index < r[j].Index
} }
type tagDetail struct {
Digest string `json:"digest"`
Name string `json:"name"`
Size int64 `json:"size"`
Architecture string `json:"architecture"`
OS string `json:"os"`
OSVersion string `json:"os.version"`
DockerVersion string `json:"docker_version"`
Author string `json:"author"`
Created time.Time `json:"created"`
Config *cfg `json:"config"`
}
type cfg struct {
Labels map[string]string `json:"labels"`
}
// TagResp holds the information of one image tag
type TagResp struct {
tagDetail
Signature *notary.Target `json:"signature"`
ScanOverview *models.ImgScanOverview `json:"scan_overview,omitempty"`
Labels []*models.Label `json:"labels"`
}
type manifestResp struct { type manifestResp struct {
Manifest interface{} `json:"manifest"` Manifest interface{} `json:"manifest"`
Config interface{} `json:"config,omitempty" ` Config interface{} `json:"config,omitempty" `
@ -610,24 +584,24 @@ func (ra *RepositoryAPI) GetTags() {
// get config, signature and scan overview and assemble them into one // get config, signature and scan overview and assemble them into one
// struct for each tag in tags // struct for each tag in tags
func assembleTagsInParallel(client *registry.Repository, repository string, func assembleTagsInParallel(client *registry.Repository, repository string,
tags []string, username string) []*TagResp { tags []string, username string) []*models.TagResp {
var err error var err error
signatures := map[string][]notary.Target{} signatures := map[string][]notarymodel.Target{}
if config.WithNotary() { if config.WithNotary() {
signatures, err = getSignatures(username, repository) signatures, err = getSignatures(username, repository)
if err != nil { if err != nil {
signatures = map[string][]notary.Target{} signatures = map[string][]notarymodel.Target{}
log.Errorf("failed to get signatures of %s: %v", repository, err) log.Errorf("failed to get signatures of %s: %v", repository, err)
} }
} }
c := make(chan *TagResp) c := make(chan *models.TagResp)
for _, tag := range tags { for _, tag := range tags {
go assembleTag(c, client, repository, tag, config.WithClair(), go assembleTag(c, client, repository, tag, config.WithClair(),
config.WithNotary(), signatures) config.WithNotary(), signatures)
} }
result := []*TagResp{} result := []*models.TagResp{}
var item *TagResp var item *models.TagResp
for i := 0; i < len(tags); i++ { for i := 0; i < len(tags); i++ {
item = <-c item = <-c
if item == nil { if item == nil {
@ -638,10 +612,10 @@ func assembleTagsInParallel(client *registry.Repository, repository string,
return result return result
} }
func assembleTag(c chan *TagResp, client *registry.Repository, func assembleTag(c chan *models.TagResp, client *registry.Repository,
repository, tag string, clairEnabled, notaryEnabled bool, repository, tag string, clairEnabled, notaryEnabled bool,
signatures map[string][]notary.Target) { signatures map[string][]notarymodel.Target) {
item := &TagResp{} item := &models.TagResp{}
// labels // labels
image := fmt.Sprintf("%s:%s", repository, tag) image := fmt.Sprintf("%s:%s", repository, tag)
labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image) labels, err := dao.GetLabelsOfResource(common.ResourceTypeImage, image)
@ -657,7 +631,7 @@ func assembleTag(c chan *TagResp, client *registry.Repository,
log.Errorf("failed to get v2 manifest of %s:%s: %v", repository, tag, err) log.Errorf("failed to get v2 manifest of %s:%s: %v", repository, tag, err)
} }
if tagDetail != nil { if tagDetail != nil {
item.tagDetail = *tagDetail item.TagDetail = *tagDetail
} }
// scan overview // scan overview
@ -680,8 +654,8 @@ func assembleTag(c chan *TagResp, client *registry.Repository,
// getTagDetail returns the detail information for v2 manifest image // getTagDetail returns the detail information for v2 manifest image
// The information contains architecture, os, author, size, etc. // The information contains architecture, os, author, size, etc.
func getTagDetail(client *registry.Repository, tag string) (*tagDetail, error) { func getTagDetail(client *registry.Repository, tag string) (*models.TagDetail, error) {
detail := &tagDetail{ detail := &models.TagDetail{
Name: tag, Name: tag,
} }
@ -738,7 +712,7 @@ func getTagDetail(client *registry.Repository, tag string) (*tagDetail, error) {
return detail, nil return detail, nil
} }
func populateAuthor(detail *tagDetail) { func populateAuthor(detail *models.TagDetail) {
// has author info already // has author info already
if len(detail.Author) > 0 { if len(detail.Author) > 0 {
return return
@ -1046,14 +1020,14 @@ func (ra *RepositoryAPI) VulnerabilityDetails() {
ra.ServeJSON() ra.ServeJSON()
} }
func getSignatures(username, repository string) (map[string][]notary.Target, error) { func getSignatures(username, repository string) (map[string][]notarymodel.Target, error) {
targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(), targets, err := notary.GetInternalTargets(config.InternalNotaryEndpoint(),
username, repository) username, repository)
if err != nil { if err != nil {
return nil, err return nil, err
} }
signatures := map[string][]notary.Target{} signatures := map[string][]notarymodel.Target{}
for _, tgt := range targets { for _, tgt := range targets {
digest, err := notary.DigestFromTarget(tgt) digest, err := notary.DigestFromTarget(tgt)
if err != nil { if err != nil {

View File

@ -96,7 +96,7 @@ func TestGetReposTags(t *testing.T) {
t.Errorf("failed to get tags of repository %s: %v", repository, err) t.Errorf("failed to get tags of repository %s: %v", repository, err)
} else { } else {
assert.Equal(int(200), code, "httpStatusCode should be 200") assert.Equal(int(200), code, "httpStatusCode should be 200")
if tg, ok := tags.([]TagResp); ok { if tg, ok := tags.([]models.TagResp); ok {
assert.Equal(1, len(tg), fmt.Sprintf("there should be only one tag, but now %v", tg)) assert.Equal(1, len(tg), fmt.Sprintf("there should be only one tag, but now %v", tg))
assert.Equal(tg[0].Name, "latest", "the tag should be latest") assert.Equal(tg[0].Name, "latest", "the tag should be latest")
} else { } else {
@ -207,19 +207,19 @@ func TestGetReposTop(t *testing.T) {
func TestPopulateAuthor(t *testing.T) { func TestPopulateAuthor(t *testing.T) {
author := "author" author := "author"
detail := &tagDetail{ detail := &models.TagDetail{
Author: author, Author: author,
} }
populateAuthor(detail) populateAuthor(detail)
assert.Equal(t, author, detail.Author) assert.Equal(t, author, detail.Author)
detail = &tagDetail{} detail = &models.TagDetail{}
populateAuthor(detail) populateAuthor(detail)
assert.Equal(t, "", detail.Author) assert.Equal(t, "", detail.Author)
maintainer := "maintainer" maintainer := "maintainer"
detail = &tagDetail{ detail = &models.TagDetail{
Config: &cfg{ Config: &models.TagCfg{
Labels: map[string]string{ Labels: map[string]string{
"Maintainer": maintainer, "Maintainer": maintainer,
}, },

View File

@ -18,10 +18,11 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/chartserver" "github.com/goharbor/harbor/src/chartserver"
chttp "github.com/goharbor/harbor/src/common/http" chttp "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/http/modifier" "github.com/goharbor/harbor/src/common/http/modifier"
"github.com/goharbor/harbor/src/core/api"
) )
// Client defines the methods that a core client should implement // Client defines the methods that a core client should implement
@ -34,7 +35,7 @@ type Client interface {
// ImageClient defines the methods that an image client should implement // ImageClient defines the methods that an image client should implement
type ImageClient interface { type ImageClient interface {
ListAllImages(project, repository string) ([]*api.TagResp, error) ListAllImages(project, repository string) ([]*models.TagResp, error)
DeleteImage(project, repository, tag string) error DeleteImage(project, repository, tag string) error
} }

View File

@ -17,12 +17,12 @@ package core
import ( import (
"fmt" "fmt"
"github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/common/models"
) )
func (c *client) ListAllImages(project, repository string) ([]*api.TagResp, error) { func (c *client) ListAllImages(project, repository string) ([]*models.TagResp, error) {
url := c.buildURL(fmt.Sprintf("/api/repositories/%s/%s/tags", project, repository)) url := c.buildURL(fmt.Sprintf("/api/repositories/%s/%s/tags", project, repository))
var images []*api.TagResp var images []*models.TagResp
if err := c.httpclient.GetAndIteratePagination(url, &images); err != nil { if err := c.httpclient.GetAndIteratePagination(url, &images); err != nil {
return nil, err return nil, err
} }

View File

@ -18,8 +18,8 @@ import (
"testing" "testing"
"github.com/goharbor/harbor/src/chartserver" "github.com/goharbor/harbor/src/chartserver"
"github.com/goharbor/harbor/src/common/job/models" jmodels "github.com/goharbor/harbor/src/common/job/models"
"github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/jobservice/job"
"github.com/goharbor/harbor/src/pkg/retention/res" "github.com/goharbor/harbor/src/pkg/retention/res"
"github.com/goharbor/harbor/src/testing/clients" "github.com/goharbor/harbor/src/testing/clients"
@ -34,10 +34,10 @@ type fakeCoreClient struct {
clients.DumbCoreClient clients.DumbCoreClient
} }
func (f *fakeCoreClient) ListAllImages(project, repository string) ([]*api.TagResp, error) { func (f *fakeCoreClient) ListAllImages(project, repository string) ([]*models.TagResp, error) {
image := &api.TagResp{} image := &models.TagResp{}
image.Name = "latest" image.Name = "latest"
return []*api.TagResp{image}, nil return []*models.TagResp{image}, nil
} }
func (f *fakeCoreClient) ListAllCharts(project, repository string) ([]*chartserver.ChartVersion, error) { func (f *fakeCoreClient) ListAllCharts(project, repository string) ([]*chartserver.ChartVersion, error) {
@ -53,7 +53,7 @@ func (f *fakeCoreClient) ListAllCharts(project, repository string) ([]*chartserv
type fakeJobserviceClient struct{} type fakeJobserviceClient struct{}
func (f *fakeJobserviceClient) SubmitJob(*models.JobData) (string, error) { func (f *fakeJobserviceClient) SubmitJob(*jmodels.JobData) (string, error) {
return "1", nil return "1", nil
} }
func (f *fakeJobserviceClient) GetJobLog(uuid string) ([]byte, error) { func (f *fakeJobserviceClient) GetJobLog(uuid string) ([]byte, error) {

View File

@ -16,7 +16,7 @@ package clients
import ( import (
"github.com/goharbor/harbor/src/chartserver" "github.com/goharbor/harbor/src/chartserver"
"github.com/goharbor/harbor/src/core/api" "github.com/goharbor/harbor/src/common/models"
) )
// DumbCoreClient provides an empty implement for pkg/clients/core.Client // DumbCoreClient provides an empty implement for pkg/clients/core.Client
@ -24,7 +24,7 @@ import (
type DumbCoreClient struct{} type DumbCoreClient struct{}
// ListAllImages ... // ListAllImages ...
func (d *DumbCoreClient) ListAllImages(project, repository string) ([]*api.TagResp, error) { func (d *DumbCoreClient) ListAllImages(project, repository string) ([]*models.TagResp, error) {
return nil, nil return nil, nil
} }