Move the scan overview populating logic to API handler

Move the scan overview populating logic to API handler to avoid importing cycle

Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
Wenkai Yin 2020-02-18 09:21:56 +08:00
parent 560dd8ce7b
commit c8c944c6e8
8 changed files with 59 additions and 140 deletions

View File

@ -478,6 +478,10 @@ definitions:
description: The error message description: The error message
Artifact: Artifact:
type: object type: object
x-go-type:
import:
package: "github.com/goharbor/harbor/src/server/v2.0/handler/model"
type: "Artifact"
properties: properties:
id: id:
type: integer type: integer

View File

@ -31,9 +31,8 @@ import (
// const definitions // const definitions
const ( const (
// ArtifactTypeImage is the artifact type for image // ArtifactTypeImage is the artifact type for image
ArtifactTypeImage = "IMAGE" ArtifactTypeImage = "IMAGE"
AdditionTypeBuildHistory = "BUILD_HISTORY" AdditionTypeBuildHistory = "BUILD_HISTORY"
AdditionTypeVulnerabilities = "VULNERABILITIES"
) )
func init() { func init() {
@ -129,5 +128,5 @@ func (m *manifestV2Resolver) GetArtifactType() string {
} }
func (m *manifestV2Resolver) ListAdditionTypes() []string { func (m *manifestV2Resolver) ListAdditionTypes() []string {
return []string{AdditionTypeBuildHistory, AdditionTypeVulnerabilities} return []string{AdditionTypeBuildHistory}
} }

View File

@ -171,7 +171,7 @@ func (m *manifestV2ResolverTestSuite) TestGetArtifactType() {
func (m *manifestV2ResolverTestSuite) TestListAdditionTypes() { func (m *manifestV2ResolverTestSuite) TestListAdditionTypes() {
additions := m.resolver.ListAdditionTypes() additions := m.resolver.ListAdditionTypes()
m.EqualValues([]string{AdditionTypeBuildHistory, AdditionTypeVulnerabilities}, additions) m.EqualValues([]string{AdditionTypeBuildHistory}, additions)
} }
func TestManifestV2ResolverTestSuite(t *testing.T) { func TestManifestV2ResolverTestSuite(t *testing.T) {

View File

@ -19,7 +19,6 @@ import (
"fmt" "fmt"
"github.com/goharbor/harbor/src/api/artifact/abstractor" "github.com/goharbor/harbor/src/api/artifact/abstractor"
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver" "github.com/goharbor/harbor/src/api/artifact/abstractor/resolver"
"github.com/goharbor/harbor/src/api/artifact/abstractor/resolver/image"
"github.com/goharbor/harbor/src/api/artifact/descriptor" "github.com/goharbor/harbor/src/api/artifact/descriptor"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/internal" "github.com/goharbor/harbor/src/internal"
@ -83,10 +82,6 @@ type Controller interface {
AddLabel(ctx context.Context, artifactID int64, labelID int64) (err error) AddLabel(ctx context.Context, artifactID int64, labelID int64) (err error)
// RemoveLabel from the specified artifact // RemoveLabel from the specified artifact
RemoveLabel(ctx context.Context, artifactID int64, labelID int64) (err error) RemoveLabel(ctx context.Context, artifactID int64, labelID int64) (err error)
// TODO move this to GC controller?
// Prune removes the useless artifact records. The underlying registry data will
// be removed during garbage collection
// Prune(ctx context.Context, option *Option) error
} }
// NewController creates an instance of the default artifact controller // NewController creates an instance of the default artifact controller
@ -350,14 +345,7 @@ func (c *controller) GetAddition(ctx context.Context, artifactID int64, addition
if err != nil { if err != nil {
return nil, err return nil, err
} }
switch addition { return c.abstractor.AbstractAddition(ctx, artifact, addition)
case image.AdditionTypeVulnerabilities:
// get the vulnerabilities from scan service
// TODO implement
return &resolver.Addition{}, nil
default:
return c.abstractor.AbstractAddition(ctx, artifact, addition)
}
} }
func (c *controller) AddLabel(ctx context.Context, artifactID int64, labelID int64) error { func (c *controller) AddLabel(ctx context.Context, artifactID int64, labelID int64) error {
@ -382,9 +370,6 @@ func (c *controller) assembleArtifact(ctx context.Context, art *artifact.Artifac
if option.WithLabel { if option.WithLabel {
c.populateLabels(ctx, artifact) c.populateLabels(ctx, artifact)
} }
if option.WithScanOverview {
c.populateScanOverview(ctx, artifact)
}
// populate addition links // populate addition links
c.populateAdditionLinks(ctx, artifact) c.populateAdditionLinks(ctx, artifact)
return artifact return artifact
@ -471,15 +456,6 @@ func (c *controller) populateImmutableStatus(ctx context.Context, tag *Tag) {
tag.Immutable = matched tag.Immutable = matched
} }
func (c *controller) populateScanOverview(ctx context.Context, art *Artifact) {
// TODO implement
}
func (c *controller) populateSignature(ctx context.Context, art *Artifact) {
// TODO implement
// TODO populate signature on artifact or tag level?
}
func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifact) { func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifact) {
types, err := descriptor.ListAdditionTypes(artifact.MediaType) types, err := descriptor.ListAdditionTypes(artifact.MediaType)
if err != nil { if err != nil {
@ -499,21 +475,11 @@ func (c *controller) populateAdditionLinks(ctx context.Context, artifact *Artifa
if artifact.AdditionLinks == nil { if artifact.AdditionLinks == nil {
artifact.AdditionLinks = make(map[string]*AdditionLink) artifact.AdditionLinks = make(map[string]*AdditionLink)
} }
href := ""
for _, t := range types { for _, t := range types {
t = strings.ToLower(t) t = strings.ToLower(t)
switch t {
case image.AdditionTypeVulnerabilities:
// check whether the scan service is enabled and set the addition link
// TODO implement
href = fmt.Sprintf("/api/%s/projects/%s/repositories/%s/artifacts/%s/vulnerabilities",
version, pro, repo, artifact.Digest)
default:
href = fmt.Sprintf("/api/%s/projects/%s/repositories/%s/artifacts/%s/additions/%s",
version, pro, repo, artifact.Digest, t)
}
artifact.AdditionLinks[t] = &AdditionLink{ artifact.AdditionLinks[t] = &AdditionLink{
HREF: href, HREF: fmt.Sprintf("/api/%s/projects/%s/repositories/%s/artifacts/%s/additions/%s",
version, pro, repo, artifact.Digest, t),
Absolute: false, Absolute: false,
} }
} }

View File

@ -129,8 +129,7 @@ func (c *controllerTestSuite) TestAssembleArtifact() {
TagOption: &TagOption{ TagOption: &TagOption{
WithImmutableStatus: false, WithImmutableStatus: false,
}, },
WithLabel: true, WithLabel: true,
WithScanOverview: true,
} }
tg := &tag.Tag{ tg := &tag.Tag{
ID: 1, ID: 1,
@ -259,8 +258,7 @@ func (c *controllerTestSuite) TestEnsure() {
func (c *controllerTestSuite) TestList() { func (c *controllerTestSuite) TestList() {
query := &q.Query{} query := &q.Query{}
option := &Option{ option := &Option{
WithTag: true, WithTag: true,
WithScanOverview: true,
} }
c.artMgr.On("List").Return(1, []*artifact.Artifact{ c.artMgr.On("List").Return(1, []*artifact.Artifact{
{ {

View File

@ -15,12 +15,10 @@
package artifact package artifact
import ( import (
"github.com/go-openapi/strfmt"
cmodels "github.com/goharbor/harbor/src/common/models" cmodels "github.com/goharbor/harbor/src/common/models"
"github.com/goharbor/harbor/src/pkg/artifact" "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/signature" "github.com/goharbor/harbor/src/pkg/signature"
"github.com/goharbor/harbor/src/pkg/tag/model/tag" "github.com/goharbor/harbor/src/pkg/tag/model/tag"
"github.com/goharbor/harbor/src/server/v2.0/models"
) )
// Artifact is the overall view of artifact // Artifact is the overall view of artifact
@ -29,77 +27,6 @@ type Artifact struct {
Tags []*Tag // the list of tags that attached to the artifact Tags []*Tag // the list of tags that attached to the artifact
AdditionLinks map[string]*AdditionLink // the resource link for build history(image), values.yaml(chart), dependency(chart), etc AdditionLinks map[string]*AdditionLink // the resource link for build history(image), values.yaml(chart), dependency(chart), etc
Labels []*cmodels.Label Labels []*cmodels.Label
// TODO add other attrs: signature, scan result, etc
}
// ToSwagger converts the artifact to the swagger model
func (a *Artifact) ToSwagger() *models.Artifact {
art := &models.Artifact{
ID: a.ID,
Type: a.Type,
MediaType: a.MediaType,
ManifestMediaType: a.ManifestMediaType,
ProjectID: a.ProjectID,
RepositoryID: a.RepositoryID,
Digest: a.Digest,
Size: a.Size,
PullTime: strfmt.DateTime(a.PullTime),
PushTime: strfmt.DateTime(a.PushTime),
ExtraAttrs: a.ExtraAttrs,
Annotations: a.Annotations,
}
for _, reference := range a.References {
ref := &models.Reference{
ChildID: reference.ChildID,
ChildDigest: reference.ChildDigest,
ParentID: reference.ParentID,
}
if reference.Platform != nil {
ref.Platform = &models.Platform{
Architecture: reference.Platform.Architecture,
Os: reference.Platform.OS,
OsFeatures: reference.Platform.OSFeatures,
OsVersion: reference.Platform.OSVersion,
Variant: reference.Platform.Variant,
}
}
art.References = append(art.References, ref)
}
for _, tag := range a.Tags {
art.Tags = append(art.Tags, &models.Tag{
ArtifactID: tag.ArtifactID,
ID: tag.ID,
Name: tag.Name,
PullTime: strfmt.DateTime(tag.PullTime),
PushTime: strfmt.DateTime(tag.PushTime),
RepositoryID: tag.RepositoryID,
Immutable: tag.Immutable,
Signed: tag.Signed,
})
}
for addition, link := range a.AdditionLinks {
if art.AdditionLinks == nil {
art.AdditionLinks = make(map[string]models.AdditionLink)
}
art.AdditionLinks[addition] = models.AdditionLink{
Absolute: link.Absolute,
Href: link.HREF,
}
}
for _, label := range a.Labels {
art.Labels = append(art.Labels, &models.Label{
ID: label.ID,
Name: label.Name,
Description: label.Description,
Color: label.Color,
CreationTime: strfmt.DateTime(label.CreationTime),
ProjectID: label.ProjectID,
Scope: label.Scope,
UpdateTime: strfmt.DateTime(label.UpdateTime),
Deleted: label.Deleted,
})
}
return art
} }
// Tag is the overall view of tag // Tag is the overall view of tag
@ -107,7 +34,6 @@ type Tag struct {
tag.Tag tag.Tag
Immutable bool Immutable bool
Signed bool Signed bool
// TODO add other attrs: label, etc
} }
// AdditionLink is a link via that the addition can be fetched // AdditionLink is a link via that the addition can be fetched
@ -118,10 +44,9 @@ type AdditionLink struct {
// Option is used to specify the properties returned when listing/getting artifacts // Option is used to specify the properties returned when listing/getting artifacts
type Option struct { type Option struct {
WithTag bool WithTag bool
TagOption *TagOption // only works when WithTag is set to true TagOption *TagOption // only works when WithTag is set to true
WithLabel bool WithLabel bool
WithScanOverview bool
} }
// TagOption is used to specify the properties returned when listing/getting tags // TagOption is used to specify the properties returned when listing/getting tags
@ -130,9 +55,3 @@ type TagOption struct {
WithSignature bool WithSignature bool
SignatureChecker *signature.Checker SignatureChecker *signature.Checker
} }
// TODO move this to GC controller?
// Option for pruning artifact records
// type Option struct {
// KeepUntagged bool // keep the untagged artifacts or not
// }

View File

@ -25,7 +25,7 @@ import (
"github.com/goharbor/harbor/src/pkg/project" "github.com/goharbor/harbor/src/pkg/project"
"github.com/goharbor/harbor/src/pkg/q" "github.com/goharbor/harbor/src/pkg/q"
"github.com/goharbor/harbor/src/pkg/repository" "github.com/goharbor/harbor/src/pkg/repository"
"github.com/goharbor/harbor/src/server/v2.0/models" "github.com/goharbor/harbor/src/server/v2.0/handler/model"
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/artifact" operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/artifact"
"net/http" "net/http"
"strings" "strings"
@ -75,7 +75,7 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
// set option // set option
option := option(params.WithTag, params.WithImmutableStatus, option := option(params.WithTag, params.WithImmutableStatus,
params.WithLabel, params.WithScanOverview, params.WithSignature) params.WithLabel, params.WithSignature)
// list artifacts according to the query and option // list artifacts according to the query and option
total, arts, err := a.artCtl.List(ctx, query, option) total, arts, err := a.artCtl.List(ctx, query, option)
@ -83,9 +83,12 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
var artifacts []*models.Artifact var artifacts []*model.Artifact
for _, art := range arts { for _, art := range arts {
artifacts = append(artifacts, art.ToSwagger()) artifact := &model.Artifact{}
artifact.Artifact = *art
a.assembleArtifact(ctx, artifact, params.WithScanOverview)
artifacts = append(artifacts, artifact)
} }
// TODO add link header // TODO add link header
@ -98,14 +101,17 @@ func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtif
} }
// set option // set option
option := option(params.WithTag, params.WithImmutableStatus, option := option(params.WithTag, params.WithImmutableStatus,
params.WithLabel, params.WithScanOverview, params.WithSignature) params.WithLabel, params.WithSignature)
// get the artifact // get the artifact
artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), params.Reference, option) artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), params.Reference, option)
if err != nil { if err != nil {
return a.SendError(ctx, err) return a.SendError(ctx, err)
} }
return operation.NewGetArtifactOK().WithPayload(artifact.ToSwagger()) art := &model.Artifact{}
art.Artifact = *artifact
a.assembleArtifact(ctx, art, params.WithScanOverview)
return operation.NewGetArtifactOK().WithPayload(art)
} }
func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder { func (a *artifactAPI) DeleteArtifact(ctx context.Context, params operation.DeleteArtifactParams) middleware.Responder {
@ -221,7 +227,14 @@ func (a *artifactAPI) RemoveLabel(ctx context.Context, params operation.RemoveLa
return operation.NewRemoveLabelOK() return operation.NewRemoveLabelOK()
} }
func option(withTag, withImmutableStatus, withLabel, withScanOverview, withSignature *bool) *artifact.Option { func (a *artifactAPI) assembleArtifact(ctx context.Context, artifact *model.Artifact, withScanOverview *bool) {
if withScanOverview != nil && *withScanOverview {
// TODO populate scan result
}
// TODO populate vulnerability link
}
func option(withTag, withImmutableStatus, withLabel, withSignature *bool) *artifact.Option {
option := &artifact.Option{ option := &artifact.Option{
WithTag: true, // return the tag by default WithTag: true, // return the tag by default
} }
@ -240,8 +253,5 @@ func option(withTag, withImmutableStatus, withLabel, withScanOverview, withSigna
if withLabel != nil { if withLabel != nil {
option.WithLabel = *(withLabel) option.WithLabel = *(withLabel)
} }
if withScanOverview != nil {
option.WithScanOverview = *(withScanOverview)
}
return option return option
} }

View File

@ -0,0 +1,23 @@
// 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/goharbor/harbor/src/api/artifact"
// Artifact model
type Artifact struct {
artifact.Artifact
// TODO add other properties: scan result
}