Compare commits

...

4 Commits

Author SHA1 Message Date
MinerYang d50e28e393
Merge 4054674e55 into fba4c40c65 2024-04-27 08:32:35 +02:00
stonezdj(Daojun Zhang) fba4c40c65
Delete scan_report when accessory is removed (#20365)
Signed-off-by: stonezdj <stone.zhang@broadcom.com>
2024-04-27 01:56:30 +00:00
Shengwen YU 9471f5d5a6
fix: update total permission count to 59 (#20352)
Signed-off-by: Shengwen Yu <yshengwen@vmware.com>
2024-04-26 08:21:27 +00:00
yminer 4054674e55 do not delete accessory relationship while still referenced
Signed-off-by: yminer <yminer@vmware.com>
2024-04-25 05:11:58 +00:00
8 changed files with 76 additions and 11 deletions

View File

@ -326,12 +326,6 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot, isAcces
return err
}
if isAccessory {
if err := c.accessoryMgr.DeleteAccessories(ctx, q.New(q.KeyWords{"ArtifactID": art.ID, "Digest": art.Digest})); err != nil && !errors.IsErr(err, errors.NotFoundCode) {
return err
}
}
// the child artifact is referenced by some tags, skip
if !isRoot && len(art.Tags) > 0 {
return nil
@ -354,6 +348,12 @@ func (c *controller) deleteDeeply(ctx context.Context, id int64, isRoot, isAcces
return nil
}
if isAccessory {
if err := c.accessoryMgr.DeleteAccessories(ctx, q.New(q.KeyWords{"ArtifactID": art.ID, "Digest": art.Digest})); err != nil && !errors.IsErr(err, errors.NotFoundCode) {
return err
}
}
// delete accessories if contains any
for _, acc := range art.Accessories {
// only hard ref accessory should be removed

View File

@ -24,6 +24,7 @@ import (
"time"
"github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/artifact/processor/sbom"
"github.com/goharbor/harbor/src/controller/event"
"github.com/goharbor/harbor/src/controller/event/operator"
"github.com/goharbor/harbor/src/controller/repository"
@ -36,6 +37,7 @@ import (
"github.com/goharbor/harbor/src/pkg"
pkgArt "github.com/goharbor/harbor/src/pkg/artifact"
"github.com/goharbor/harbor/src/pkg/scan/report"
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
"github.com/goharbor/harbor/src/pkg/task"
)
@ -319,6 +321,11 @@ func (a *ArtifactEventHandler) onDelete(ctx context.Context, event *event.Artifa
log.Errorf("failed to delete scan reports of artifact %v, error: %v", unrefDigests, err)
}
if event.Artifact.Type == sbom.ArtifactTypeSBOM && len(event.Artifact.Digest) > 0 {
if err := reportMgr.DeleteByExtraAttr(ctx, v1.MimeTypeSBOMReport, "sbom_digest", event.Artifact.Digest); err != nil {
log.Errorf("failed to delete scan reports of with sbom digest %v, error: %v", event.Artifact.Digest, err)
}
}
return nil
}

View File

@ -16,6 +16,7 @@ package scan
import (
"context"
"fmt"
"github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/orm"
@ -38,6 +39,8 @@ type DAO interface {
UpdateReportData(ctx context.Context, uuid string, report string) error
// Update update report
Update(ctx context.Context, r *Report, cols ...string) error
// DeleteByExtraAttr delete the scan_report by mimeType and extra attribute
DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error
}
// New returns an instance of the default DAO
@ -110,3 +113,14 @@ func (d *dao) Update(ctx context.Context, r *Report, cols ...string) error {
}
return nil
}
func (d *dao) DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error {
o, err := orm.FromContext(ctx)
if err != nil {
return err
}
delReportSQL := "delete from scan_report where mime_type = ? and report::jsonb @> ?"
dgstJSONStr := fmt.Sprintf(`{"%s":"%s"}`, attrName, attrValue)
_, err = o.Raw(delReportSQL, mimeType, dgstJSONStr).Exec()
return err
}

View File

@ -53,14 +53,23 @@ func (suite *ReportTestSuite) SetupTest() {
RegistrationUUID: "ruuid",
MimeType: v1.MimeTypeNativeReport,
}
suite.create(r)
sbomReport := &Report{
UUID: "uuid3",
Digest: "digest1003",
RegistrationUUID: "ruuid",
MimeType: v1.MimeTypeSBOMReport,
Report: `{"sbom_digest": "sha256:abc"}`,
}
suite.create(sbomReport)
}
// TearDownTest clears enf for test case.
func (suite *ReportTestSuite) TearDownTest() {
_, err := suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid"}})
require.NoError(suite.T(), err)
_, err = suite.dao.DeleteMany(orm.Context(), q.Query{Keywords: q.KeyWords{"uuid": "uuid3"}})
require.NoError(suite.T(), err)
}
// TestReportList tests list reports with query parameters.
@ -95,7 +104,7 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
err := suite.dao.UpdateReportData(orm.Context(), "uuid", "{}")
suite.Require().NoError(err)
l, err := suite.dao.List(orm.Context(), nil)
l, err := suite.dao.List(orm.Context(), q.New(q.KeyWords{"uuid": "uuid"}))
suite.Require().NoError(err)
suite.Require().Equal(1, len(l))
suite.Equal("{}", l[0].Report)
@ -104,6 +113,17 @@ func (suite *ReportTestSuite) TestReportUpdateReportData() {
suite.Require().NoError(err)
}
func (suite *ReportTestSuite) TestDeleteReportBySBOMDigest() {
l, err := suite.dao.List(orm.Context(), nil)
suite.Require().NoError(err)
suite.Equal(2, len(l))
err = suite.dao.DeleteByExtraAttr(orm.Context(), v1.MimeTypeSBOMReport, "sbom_digest", "sha256:abc")
suite.Require().NoError(err)
l2, err := suite.dao.List(orm.Context(), nil)
suite.Require().NoError(err)
suite.Equal(1, len(l2))
}
func (suite *ReportTestSuite) create(r *Report) {
id, err := suite.dao.Create(orm.Context(), r)
suite.Require().NoError(err)

View File

@ -104,6 +104,8 @@ type Manager interface {
// Update update report information
Update(ctx context.Context, r *scan.Report, cols ...string) error
// DeleteByExtraAttr delete scan_report by sbom_digest
DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error
}
// basicManager is a default implementation of report manager.
@ -226,3 +228,7 @@ func (bm *basicManager) List(ctx context.Context, query *q.Query) ([]*scan.Repor
func (bm *basicManager) Update(ctx context.Context, r *scan.Report, cols ...string) error {
return bm.dao.Update(ctx, r, cols...)
}
func (bm *basicManager) DeleteByExtraAttr(ctx context.Context, mimeType, attrName, attrValue string) error {
return bm.dao.DeleteByExtraAttr(ctx, mimeType, attrName, attrValue)
}

View File

@ -87,6 +87,24 @@ func (_m *Manager) DeleteByDigests(ctx context.Context, digests ...string) error
return r0
}
// DeleteByExtraAttr provides a mock function with given fields: ctx, mimeType, attrName, attrValue
func (_m *Manager) DeleteByExtraAttr(ctx context.Context, mimeType string, attrName string, attrValue string) error {
ret := _m.Called(ctx, mimeType, attrName, attrValue)
if len(ret) == 0 {
panic("no return value specified for DeleteByExtraAttr")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
r0 = rf(ctx, mimeType, attrName, attrValue)
} else {
r0 = ret.Error(0)
}
return r0
}
// GetBy provides a mock function with given fields: ctx, digest, registrationUUID, mimeTypes
func (_m *Manager) GetBy(ctx context.Context, digest string, registrationUUID string, mimeTypes []string) ([]*scan.Report, error) {
ret := _m.Called(ctx, digest, registrationUUID, mimeTypes)

View File

@ -24,8 +24,8 @@ Create A Project Robot Account
${permission_count}= Create Dictionary
${total}= Set Variable 0
IF '${first_resource}' == 'all'
Set To Dictionary ${permission_count} all=56
${total}= Set Variable 56
Set To Dictionary ${permission_count} all=59
${total}= Set Variable 59
Retry Element Click //span[text()='Select all']
ELSE
FOR ${item} IN @{resources}

View File

@ -745,7 +745,7 @@ Test Case - System Robot Account
${robot_account_name} ${token}= Create A System Robot Account sys2${d} days days=2 description=For testing cover_all_project_resources=${true}
Push image ${ip} '${robot_account_name}' ${token} project${d} hello-world:latest
Retry Wait Element Visible //clr-dg-row[.//clr-dg-cell[contains(.,'${robot_account_name}')] and .//clr-icon[contains(@class, 'color-green')] and .//span[text()='All projects with'] and .//button[text()=' 56 PERMISSION(S) '] and .//span[contains(.,'1d 23h')] and .//clr-dg-cell[text()='For testing'] and .//clr-dg-cell//span[text()=' None ']]
Retry Wait Element Visible //clr-dg-row[.//clr-dg-cell[contains(.,'${robot_account_name}')] and .//clr-icon[contains(@class, 'color-green')] and .//span[text()='All projects with'] and .//button[text()=' 59 PERMISSION(S) '] and .//span[contains(.,'1d 23h')] and .//clr-dg-cell[text()='For testing'] and .//clr-dg-cell//span[text()=' None ']]
Retry Action Keyword Check System Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} all 1
Retry Action Keyword Check Project Robot Account API Permission ${robot_account_name} ${token} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} ${project_id} ${project_name} hello-world latest all
Close Browser