From 96ba34a93c3d5d8bd80c9585958ff0cc3811ba57 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Tue, 9 Apr 2024 10:24:57 +0800 Subject: [PATCH 001/100] Allow empty path in redirect_url (#20238) fixes #20226 Signed-off-by: stonezdj Co-authored-by: stonezdj --- src/common/utils/utils.go | 6 +++--- src/common/utils/utils_test.go | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/utils/utils.go b/src/common/utils/utils.go index 07597492e..9a7a1f07c 100644 --- a/src/common/utils/utils.go +++ b/src/common/utils/utils.go @@ -313,11 +313,11 @@ func ValidateCronString(cron string) error { // sort.Slice(input, func(i, j int) bool { // return MostMatchSorter(input[i].GroupName, input[j].GroupName, matchWord) // }) +// // a is the field to be used for sorting, b is the other field, matchWord is the word to be matched // the return value is true if a is less than b // for example, search with "user", input is {"harbor_user", "user", "users, "admin_user"} // it returns with this order {"user", "users", "admin_user", "harbor_user"} - func MostMatchSorter(a, b string, matchWord string) bool { // exact match always first if a == matchWord { @@ -333,7 +333,7 @@ func MostMatchSorter(a, b string, matchWord string) bool { return len(a) < len(b) } -// IsLocalPath checks if path is local +// IsLocalPath checks if path is local, includes the empty path func IsLocalPath(path string) bool { - return strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "//") + return len(path) == 0 || (strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "//")) } diff --git a/src/common/utils/utils_test.go b/src/common/utils/utils_test.go index 8849e1f0c..4e1ab2ef3 100644 --- a/src/common/utils/utils_test.go +++ b/src/common/utils/utils_test.go @@ -501,6 +501,7 @@ func TestIsLocalPath(t *testing.T) { {"other_site1", args{"//www.myexample.com"}, false}, {"other_site2", args{"https://www.myexample.com"}, false}, {"other_site", args{"http://www.myexample.com"}, false}, + {"empty_path", args{""}, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From ff1a5056d799217675fdde5c08fa0ac34894fccc Mon Sep 17 00:00:00 2001 From: guangwu Date: Tue, 9 Apr 2024 14:27:46 +0800 Subject: [PATCH 002/100] fix: close blob io ReadCloser (#20225) Signed-off-by: guoguangwu Co-authored-by: Wang Yan --- src/controller/artifact/annotation/v1alpha1.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/controller/artifact/annotation/v1alpha1.go b/src/controller/artifact/annotation/v1alpha1.go index 15c464ab2..6ca4605e1 100644 --- a/src/controller/artifact/annotation/v1alpha1.go +++ b/src/controller/artifact/annotation/v1alpha1.go @@ -92,6 +92,7 @@ func parseV1alpha1Icon(artifact *artifact.Artifact, manifest *v1.Manifest, reg r if err != nil { return err } + defer icon.Close() // check the size of the size <= 1MB data, err := io.ReadAll(io.LimitReader(icon, 1<<20)) if err != nil { From be648ea47f0e987f3b48235d6231a9248e81d166 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Tue, 9 Apr 2024 16:05:30 +0800 Subject: [PATCH 003/100] Refactor scan job service make it easy to add new scan type (#20177) Signed-off-by: stonezdj Signed-off-by: stonezdj(Daojun Zhang) Co-authored-by: stonezdj --- src/controller/scan/base_controller.go | 23 +++---- src/controller/scan/base_controller_test.go | 1 + src/controller/scan/options.go | 4 +- src/core/main.go | 1 + src/jobservice/main.go | 1 + src/pkg/scan/handler.go | 44 +++++++++++++ src/pkg/scan/job.go | 70 ++++++++++---------- src/pkg/scan/job_test.go | 71 ++++++++++++++++++++- src/pkg/scan/rest/v1/models.go | 19 ++++++ src/pkg/scan/util.go | 4 +- src/pkg/scan/util_test.go | 11 ++-- src/pkg/scan/vuln/report.go | 3 + src/pkg/scan/vulnerability/vul.go | 57 +++++++++++++++++ src/pkg/scan/vulnerability/vul_test.go | 52 +++++++++++++++ 14 files changed, 303 insertions(+), 58 deletions(-) create mode 100644 src/pkg/scan/handler.go create mode 100644 src/pkg/scan/vulnerability/vul.go create mode 100644 src/pkg/scan/vulnerability/vul_test.go diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index e79896959..be2b371d4 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -25,7 +25,6 @@ import ( "github.com/google/uuid" - "github.com/goharbor/harbor/src/common/rbac" ar "github.com/goharbor/harbor/src/controller/artifact" "github.com/goharbor/harbor/src/controller/event/operator" "github.com/goharbor/harbor/src/controller/robot" @@ -91,6 +90,7 @@ type launchScanJobParam struct { Artifact *ar.Artifact Tag string Reports []*scan.Report + Type string } // basicController is default implementation of api.Controller interface @@ -287,6 +287,7 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti Artifact: art, Tag: tag, Reports: reports, + Type: opts.GetScanType(), }) } } @@ -912,7 +913,7 @@ func (bc *basicController) GetVulnerable(ctx context.Context, artifact *ar.Artif } // makeRobotAccount creates a robot account based on the arguments for scanning. -func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64, repository string, registration *scanner.Registration) (*robot.Robot, error) { +func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64, repository string, registration *scanner.Registration, permission []*types.Policy) (*robot.Robot, error) { // Use uuid as name to avoid duplicated entries. UUID, err := bc.uuid() if err != nil { @@ -934,16 +935,7 @@ func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64 { Kind: "project", Namespace: projectName, - Access: []*types.Policy{ - { - Resource: rbac.ResourceRepository, - Action: rbac.ActionPull, - }, - { - Resource: rbac.ResourceRepository, - Action: rbac.ActionScannerPull, - }, - }, + Access: permission, }, }, } @@ -980,7 +972,12 @@ func (bc *basicController) launchScanJob(ctx context.Context, param *launchScanJ return errors.Wrap(err, "scan controller: launch scan job") } - robot, err := bc.makeRobotAccount(ctx, param.Artifact.ProjectID, param.Artifact.RepositoryName, param.Registration) + // Get Scanner handler by scan type to separate the scan logic for different scan types + handler := sca.GetScanHandler(param.Type) + if handler == nil { + return fmt.Errorf("failed to get scan handler, type is %v", param.Type) + } + robot, err := bc.makeRobotAccount(ctx, param.Artifact.ProjectID, param.Artifact.RepositoryName, param.Registration, handler.RequiredPermissions()) if err != nil { return errors.Wrap(err, "scan controller: launch scan job") } diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index 2858b7090..ced106577 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -45,6 +45,7 @@ import ( "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" "github.com/goharbor/harbor/src/pkg/scan/vuln" + _ "github.com/goharbor/harbor/src/pkg/scan/vulnerability" "github.com/goharbor/harbor/src/pkg/task" artifacttesting "github.com/goharbor/harbor/src/testing/controller/artifact" robottesting "github.com/goharbor/harbor/src/testing/controller/robot" diff --git a/src/controller/scan/options.go b/src/controller/scan/options.go index f62e2205e..82e4e3d3e 100644 --- a/src/controller/scan/options.go +++ b/src/controller/scan/options.go @@ -14,6 +14,8 @@ package scan +import v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + // Options keep the settings/configurations for scanning. type Options struct { ExecutionID int64 // The execution id to scan artifact @@ -24,7 +26,7 @@ type Options struct { // GetScanType returns the scan type. for backward compatibility, the default type is vulnerability. func (o *Options) GetScanType() string { if len(o.ScanType) == 0 { - o.ScanType = "vulnerability" + o.ScanType = v1.ScanTypeVulnerability } return o.ScanType } diff --git a/src/core/main.go b/src/core/main.go index 50e6a4566..cb2676135 100644 --- a/src/core/main.go +++ b/src/core/main.go @@ -70,6 +70,7 @@ import ( "github.com/goharbor/harbor/src/pkg/oidc" "github.com/goharbor/harbor/src/pkg/scan" "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" + _ "github.com/goharbor/harbor/src/pkg/scan/vulnerability" pkguser "github.com/goharbor/harbor/src/pkg/user" "github.com/goharbor/harbor/src/pkg/version" "github.com/goharbor/harbor/src/server" diff --git a/src/jobservice/main.go b/src/jobservice/main.go index 42d531546..f288efea7 100644 --- a/src/jobservice/main.go +++ b/src/jobservice/main.go @@ -36,6 +36,7 @@ import ( _ "github.com/goharbor/harbor/src/pkg/accessory/model/subject" _ "github.com/goharbor/harbor/src/pkg/config/inmemory" _ "github.com/goharbor/harbor/src/pkg/config/rest" + _ "github.com/goharbor/harbor/src/pkg/scan/vulnerability" ) func main() { diff --git a/src/pkg/scan/handler.go b/src/pkg/scan/handler.go new file mode 100644 index 000000000..7ddba595d --- /dev/null +++ b/src/pkg/scan/handler.go @@ -0,0 +1,44 @@ +// 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 scan + +import ( + "time" + + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/pkg/permission/types" + "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/scan/dao/scan" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" +) + +var handlerRegistry = map[string]Handler{} + +// RegisterScanHanlder register scanner handler +func RegisterScanHanlder(requestType string, handler Handler) { + handlerRegistry[requestType] = handler +} + +// GetScanHandler get the handler +func GetScanHandler(requestType string) Handler { + return handlerRegistry[requestType] +} + +// Handler handler for scan job, it could be implement by different scan type, such as vulnerability, sbom +type Handler interface { + RequiredPermissions() []*types.Policy + // PostScan defines the operation after scan + PostScan(ctx job.Context, sr *v1.ScanRequest, rp *scan.Report, rawReport string, startTime time.Time, robot *model.Robot) (string, error) +} diff --git a/src/pkg/scan/job.go b/src/pkg/scan/job.go index b1ad1905d..c21b48cb2 100644 --- a/src/pkg/scan/job.go +++ b/src/pkg/scan/job.go @@ -16,6 +16,7 @@ package scan import ( "bytes" + "context" "encoding/base64" "encoding/json" "fmt" @@ -34,8 +35,8 @@ import ( "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/scan/dao/scan" "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" - "github.com/goharbor/harbor/src/pkg/scan/postprocessors" "github.com/goharbor/harbor/src/pkg/scan/report" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" ) @@ -145,6 +146,7 @@ func (j *Job) Validate(params job.Parameters) error { func (j *Job) Run(ctx job.Context, params job.Parameters) error { // Get logger myLogger := ctx.GetLogger() + startTime := time.Now() // shouldStop checks if the job should be stopped shouldStop := func() bool { @@ -160,6 +162,11 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { r, _ := extractRegistration(params) req, _ := ExtractScanReq(params) mimeTypes, _ := extractMimeTypes(params) + scanType := v1.ScanTypeVulnerability + if len(req.RequestType) > 0 { + scanType = req.RequestType[0].Type + } + handler := GetScanHandler(scanType) // Print related infos to log printJSONParameter(JobParamRegistration, removeRegistrationAuthInfo(r), myLogger) @@ -235,30 +242,19 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { } myLogger.Debugf("check scan report for mime %s at %s", m, t.Format("2006/01/02 15:04:05")) - - rawReport, err := client.GetScanReport(resp.ID, m) + rawReport, err := fetchScanReportFromScanner(client, resp.ID, m) if err != nil { // Not ready yet if notReadyErr, ok := err.(*v1.ReportNotReadyError); ok { // Reset to the new check interval tm.Reset(time.Duration(notReadyErr.RetryAfter) * time.Second) myLogger.Infof("Report with mime type %s is not ready yet, retry after %d seconds", m, notReadyErr.RetryAfter) - continue } - - errs[i] = errors.Wrap(err, fmt.Sprintf("check scan report with mime type %s", m)) + errs[i] = errors.Wrap(err, fmt.Sprintf("scan job: fetch scan report, mimetype %v", m)) return } - - // Make sure the data is aligned with the v1 spec. - if _, err = report.ResolveData(m, []byte(rawReport)); err != nil { - errs[i] = errors.Wrap(err, "scan job: resolve report data") - return - } - rawReports[i] = rawReport - return case <-ctx.SystemContext().Done(): // Terminated by system @@ -292,33 +288,19 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { // Log error to the job log if err != nil { myLogger.Error(err) - return err } for i, mimeType := range mimeTypes { - reports, err := report.Mgr.GetBy(ctx.SystemContext(), req.Artifact.Digest, r.UUID, []string{mimeType}) + rp, err := getReportPlaceholder(ctx.SystemContext(), req.Artifact.Digest, r.UUID, mimeType, myLogger) if err != nil { - myLogger.Error("Failed to get report for artifact %s of mimetype %s, error %v", req.Artifact.Digest, mimeType, err) - return err } + myLogger.Debugf("Converting report ID %s to the new V2 schema", rp.UUID) - if len(reports) == 0 { - myLogger.Error("No report found for artifact %s of mimetype %s, error %v", req.Artifact.Digest, mimeType, err) - - return errors.NotFoundError(nil).WithMessage("no report found to update data") - } - - rp := reports[0] - - logger.Debugf("Converting report ID %s to the new V2 schema", rp.UUID) - - // use a new ormer here to use the short db connection - _, reportData, err := postprocessors.Converter.ToRelationalSchema(ctx.SystemContext(), rp.UUID, rp.RegistrationUUID, rp.Digest, rawReports[i]) + reportData, err := handler.PostScan(ctx, req, rp, rawReports[i], startTime, robotAccount) if err != nil { myLogger.Errorf("Failed to convert vulnerability data to new schema for report %s, error %v", rp.UUID, err) - return err } @@ -328,7 +310,6 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { // would be redundant if err := report.Mgr.UpdateReportData(ctx.SystemContext(), rp.UUID, reportData); err != nil { myLogger.Errorf("Failed to update report data for report %s, error %v", rp.UUID, err) - return err } @@ -338,6 +319,31 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { return nil } +func getReportPlaceholder(ctx context.Context, digest string, reportUUID string, mimeType string, logger logger.Interface) (*scan.Report, error) { + reports, err := report.Mgr.GetBy(ctx, digest, reportUUID, []string{mimeType}) + if err != nil { + logger.Error("Failed to get report for artifact %s of mimetype %s, error %v", digest, mimeType, err) + return nil, err + } + if len(reports) == 0 { + logger.Errorf("No report found for artifact %s of mimetype %s, error %v", digest, mimeType, err) + return nil, errors.NotFoundError(nil).WithMessage("no report found to update data") + } + return reports[0], nil +} + +func fetchScanReportFromScanner(client v1.Client, requestID string, m string) (rawReport string, err error) { + rawReport, err = client.GetScanReport(requestID, m) + if err != nil { + return "", err + } + // Make sure the data is aligned with the v1 spec. + if _, err = report.ResolveData(m, []byte(rawReport)); err != nil { + return "", err + } + return rawReport, nil +} + // ExtractScanReq extracts the scan request from the job parameters. func ExtractScanReq(params job.Parameters) (*v1.ScanRequest, error) { v, ok := params[JobParameterRequest] diff --git a/src/pkg/scan/job_test.go b/src/pkg/scan/job_test.go index d29c35fde..92571285d 100644 --- a/src/pkg/scan/job_test.go +++ b/src/pkg/scan/job_test.go @@ -19,15 +19,19 @@ import ( "testing" "time" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/goharbor/harbor/src/controller/robot" "github.com/goharbor/harbor/src/jobservice/job" "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/scan/dao/scan" "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" + "github.com/goharbor/harbor/src/pkg/scan/report" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" "github.com/goharbor/harbor/src/pkg/scan/vuln" + htesting "github.com/goharbor/harbor/src/testing" mockjobservice "github.com/goharbor/harbor/src/testing/jobservice" mocktesting "github.com/goharbor/harbor/src/testing/mock" v1testing "github.com/goharbor/harbor/src/testing/pkg/scan/rest/v1" @@ -35,10 +39,11 @@ import ( // JobTestSuite is a test suite to test the scan job. type JobTestSuite struct { - suite.Suite + htesting.Suite defaultClientPool v1.ClientPool mcp *v1testing.ClientPool + reportIDs []string } // TestJob is the entry of JobTestSuite. @@ -48,6 +53,7 @@ func TestJob(t *testing.T) { // SetupSuite sets up test env for JobTestSuite. func (suite *JobTestSuite) SetupSuite() { + suite.Suite.SetupSuite() mcp := &v1testing.ClientPool{} suite.defaultClientPool = v1.DefaultClientPool v1.DefaultClientPool = mcp @@ -55,9 +61,12 @@ func (suite *JobTestSuite) SetupSuite() { suite.mcp = mcp } -// TeraDownSuite clears test env for TeraDownSuite. -func (suite *JobTestSuite) TeraDownSuite() { +// TearDownSuite clears test env for TearDownSuite. +func (suite *JobTestSuite) TearDownSuite() { v1.DefaultClientPool = suite.defaultClientPool + for _, id := range suite.reportIDs { + _ = report.Mgr.Delete(suite.Context(), id) + } } // TestJob tests the scan job @@ -151,3 +160,59 @@ func (suite *JobTestSuite) TestJob() { err = j.Run(ctx, jp) require.NoError(suite.T(), err) } + +func (suite *JobTestSuite) TestgetReportPlaceholder() { + dgst := "sha256:mydigest" + uuid := `7f20b1b9-6117-4a2e-820b-e4cc0401f15e` + scannerUUID := `7f20b1b9-6117-4a2e-820b-e4cc0401f15f` + rpt := &scan.Report{ + UUID: uuid, + RegistrationUUID: scannerUUID, + Digest: dgst, + MimeType: v1.MimeTypeDockerArtifact, + } + ctx := suite.Context() + rptID, err := report.Mgr.Create(ctx, rpt) + suite.reportIDs = append(suite.reportIDs, rptID) + require.NoError(suite.T(), err) + jobLogger := &mockjobservice.MockJobLogger{} + report, err := getReportPlaceholder(ctx, dgst, scannerUUID, v1.MimeTypeDockerArtifact, jobLogger) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), report) +} + +func (suite *JobTestSuite) TestfetchScanReportFromScanner() { + vulnRpt := &vuln.Report{ + GeneratedAt: time.Now().UTC().String(), + Scanner: &v1.Scanner{ + Name: "Trivy", + Vendor: "Harbor", + Version: "0.1.0", + }, + Severity: vuln.High, + } + rptContent, err := json.Marshal(vulnRpt) + require.NoError(suite.T(), err) + rawContent := string(rptContent) + ctx := suite.Context() + dgst := "sha256:mydigest" + uuid := `7f20b1b9-6117-4a2e-820b-e4cc0401f15a` + scannerUUID := `7f20b1b9-6117-4a2e-820b-e4cc0401f15b` + rpt := &scan.Report{ + UUID: uuid, + RegistrationUUID: scannerUUID, + Digest: dgst, + MimeType: v1.MimeTypeDockerArtifact, + Report: rawContent, + } + + ctx = suite.Context() + rptID, err := report.Mgr.Create(ctx, rpt) + suite.reportIDs = append(suite.reportIDs, rptID) + require.NoError(suite.T(), err) + client := &v1testing.Client{} + client.On("GetScanReport", mock.Anything, v1.MimeTypeGenericVulnerabilityReport).Return(rawContent, nil) + rawRept, err := fetchScanReportFromScanner(client, "abc", v1.MimeTypeGenericVulnerabilityReport) + require.NoError(suite.T(), err) + require.Equal(suite.T(), rawContent, rawRept) +} diff --git a/src/pkg/scan/rest/v1/models.go b/src/pkg/scan/rest/v1/models.go index d7dce069e..fc48717fb 100644 --- a/src/pkg/scan/rest/v1/models.go +++ b/src/pkg/scan/rest/v1/models.go @@ -21,6 +21,13 @@ import ( "github.com/goharbor/harbor/src/lib/errors" ) +const ( + // ScanTypeVulnerability the scan type for vulnerability + ScanTypeVulnerability = "vulnerability" + // ScanTypeSbom the scan type for sbom + ScanTypeSbom = "sbom" +) + // Scanner represents metadata of a Scanner Adapter which allow Harbor to lookup a scanner capable of // scanning a given Artifact stored in its registry and making sure that it can interpret a // returned result. @@ -173,6 +180,18 @@ type ScanRequest struct { Registry *Registry `json:"registry"` // Artifact to be scanned. Artifact *Artifact `json:"artifact"` + // RequestType + RequestType []*ScanType `json:"enabled_capabilities"` +} + +// ScanType represent the type of the scan request +type ScanType struct { + // Type sets the type of the scan, it could be sbom or vulnerability, default is vulnerability + Type string `json:"type"` + // ProducesMimeTypes defines scanreport should be + ProducesMimeTypes []string `json:"produces_mime_types"` + // Parameters extra parameters + Parameters map[string]interface{} `json:"parameters"` } // FromJSON parses ScanRequest from json data diff --git a/src/pkg/scan/util.go b/src/pkg/scan/util.go index ac19d92a0..efec3eb61 100644 --- a/src/pkg/scan/util.go +++ b/src/pkg/scan/util.go @@ -30,7 +30,7 @@ import ( "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/goharbor/harbor/src/controller/robot" + "github.com/goharbor/harbor/src/pkg/robot/model" v1sq "github.com/goharbor/harbor/src/pkg/scan/rest/v1" ) @@ -49,7 +49,7 @@ type referrer struct { } // GenAccessoryArt composes the accessory oci object and push it back to harbor core as an accessory of the scanned artifact. -func GenAccessoryArt(sq v1sq.ScanRequest, accData []byte, accAnnotations map[string]string, mediaType string, robot robot.Robot) (string, error) { +func GenAccessoryArt(sq v1sq.ScanRequest, accData []byte, accAnnotations map[string]string, mediaType string, robot *model.Robot) (string, error) { accArt, err := mutate.Append(empty.Image, mutate.Addendum{ Layer: static.NewLayer(accData, ocispec.MediaTypeImageLayer), History: v1.History{ diff --git a/src/pkg/scan/util_test.go b/src/pkg/scan/util_test.go index 519d6d68a..b53fad834 100644 --- a/src/pkg/scan/util_test.go +++ b/src/pkg/scan/util_test.go @@ -22,8 +22,7 @@ import ( "github.com/google/go-containerregistry/pkg/registry" "github.com/stretchr/testify/assert" - "github.com/goharbor/harbor/src/controller/robot" - rm "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/robot/model" v1sq "github.com/goharbor/harbor/src/pkg/scan/rest/v1" ) @@ -47,11 +46,9 @@ func TestGenAccessoryArt(t *testing.T) { Digest: "sha256:d37ada95d47ad12224c205a938129df7a3e52345828b4fa27b03a98825d1e2e7", }, } - r := robot.Robot{ - Robot: rm.Robot{ - Name: "admin", - Secret: "Harbor12345", - }, + r := &model.Robot{ + Name: "admin", + Secret: "Harbor12345", } annotations := map[string]string{ diff --git a/src/pkg/scan/vuln/report.go b/src/pkg/scan/vuln/report.go index 0f24737b9..fbc119f64 100644 --- a/src/pkg/scan/vuln/report.go +++ b/src/pkg/scan/vuln/report.go @@ -33,6 +33,9 @@ type Report struct { Vulnerabilities []*VulnerabilityItem `json:"vulnerabilities"` vulnerabilityItemList *VulnerabilityItemList + + // SBOM sbom content + SBOM map[string]interface{} `json:"sbom,omitempty"` } // GetVulnerabilityItemList returns VulnerabilityItemList from the Vulnerabilities of report diff --git a/src/pkg/scan/vulnerability/vul.go b/src/pkg/scan/vulnerability/vul.go new file mode 100644 index 000000000..804659c09 --- /dev/null +++ b/src/pkg/scan/vulnerability/vul.go @@ -0,0 +1,57 @@ +// 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 vulnerability + +import ( + "time" + + "github.com/goharbor/harbor/src/common/rbac" + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/pkg/permission/types" + "github.com/goharbor/harbor/src/pkg/robot/model" + scanJob "github.com/goharbor/harbor/src/pkg/scan" + "github.com/goharbor/harbor/src/pkg/scan/dao/scan" + "github.com/goharbor/harbor/src/pkg/scan/postprocessors" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" +) + +func init() { + scanJob.RegisterScanHanlder(v1.ScanTypeVulnerability, &ScanHandler{}) +} + +// ScanHandler defines the handler for scan vulnerability +type ScanHandler struct { +} + +// RequiredPermissions defines the permission used by the scan robot account +func (v *ScanHandler) RequiredPermissions() []*types.Policy { + return []*types.Policy{ + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionScannerPull, + }, + } +} + +// PostScan ... +func (v *ScanHandler) PostScan(ctx job.Context, _ *v1.ScanRequest, origRp *scan.Report, rawReport string, _ time.Time, _ *model.Robot) (string, error) { + // use a new ormer here to use the short db connection + _, refreshedReport, err := postprocessors.Converter.ToRelationalSchema(ctx.SystemContext(), origRp.UUID, origRp.RegistrationUUID, origRp.Digest, rawReport) + return refreshedReport, err +} diff --git a/src/pkg/scan/vulnerability/vul_test.go b/src/pkg/scan/vulnerability/vul_test.go new file mode 100644 index 000000000..50d84287e --- /dev/null +++ b/src/pkg/scan/vulnerability/vul_test.go @@ -0,0 +1,52 @@ +package vulnerability + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "github.com/goharbor/harbor/src/common/rbac" + "github.com/goharbor/harbor/src/pkg/permission/types" + "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/scan/dao/scan" + "github.com/goharbor/harbor/src/pkg/scan/postprocessors" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + "github.com/goharbor/harbor/src/testing/jobservice" + postprocessorstesting "github.com/goharbor/harbor/src/testing/pkg/scan/postprocessors" +) + +func TestRequiredPermissions(t *testing.T) { + v := &ScanHandler{} + expected := []*types.Policy{ + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionScannerPull, + }, + } + + result := v.RequiredPermissions() + + assert.Equal(t, expected, result, "RequiredPermissions should return correct permissions") +} + +func TestPostScan(t *testing.T) { + v := &ScanHandler{} + ctx := &jobservice.MockJobContext{} + artifact := &v1.Artifact{} + origRp := &scan.Report{} + rawReport := "" + + mocker := &postprocessorstesting.ScanReportV1ToV2Converter{} + mocker.On("ToRelationalSchema", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, "original report", nil) + postprocessors.Converter = mocker + sr := &v1.ScanRequest{Artifact: artifact} + refreshedReport, err := v.PostScan(ctx, sr, origRp, rawReport, time.Now(), &model.Robot{}) + assert.Equal(t, "", refreshedReport, "PostScan should return the refreshed report") + assert.Nil(t, err, "PostScan should not return an error") +} From 461a5fa50da856f5d5f940e64bfbc685607d05fb Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Tue, 9 Apr 2024 16:07:47 +0800 Subject: [PATCH 004/100] add stop sbom scanning API (#20200) * add stop sbom scanning API 1. [UI] support to stop sbom scanning #20200 2. add type for stop scanning api, make it able to support both vulnerability and sbom. 3. refactor the db query to support multiple extra attributes. Signed-off-by: wang yan Signed-off-by: xuelichao Co-authored-by: xuelichao --- api/v2.0/swagger.yaml | 14 + src/controller/scan/base_controller.go | 5 +- src/controller/scan/base_controller_test.go | 8 +- src/controller/scan/controller.go | 3 +- src/pkg/task/dao/execution.go | 120 ++++--- src/pkg/task/dao/execution_test.go | 32 +- .../artifact/artifact-additions/models.ts | 1 + .../artifact-list-page.service.spec.ts | 93 ++++- .../artifact-list-page.service.ts | 77 ++++- .../artifact-list-tab.component.html | 62 +++- .../artifact-list-tab.component.scss | 4 + .../artifact-list-tab.component.spec.ts | 133 ++++++- .../artifact-list-tab.component.ts | 147 +++++++- .../repository/artifact/artifact.module.ts | 4 + .../artifact/sbom-scanning/sbom-overview.ts | 39 +++ .../sbom-scanning/sbom-scan-component.html | 38 ++ .../sbom-scanning/sbom-scan.component.spec.ts | 245 +++++++++++++ .../sbom-scanning/sbom-scan.component.ts | 327 ++++++++++++++++++ .../sbom-tip-histogram.component.html | 78 +++++ .../sbom-tip-histogram.component.scss | 60 ++++ .../sbom-tip-histogram.component.spec.ts | 103 ++++++ .../sbom-tip-histogram.component.ts | 111 ++++++ .../artifact/sbom-scanning/scanning.scss | 172 +++++++++ .../result-bar-chart.component.spec.ts | 157 +++++++++ .../result-bar-chart.component.ts | 10 +- .../vulnerability-scanning/scanning.scss | 2 +- .../services/event-service/event.service.ts | 3 + ...rtifact-detail-routing-resolver.service.ts | 3 +- .../src/app/shared/entities/shared.const.ts | 5 + .../src/app/shared/services/interface.ts | 10 + .../app/shared/services/permission-static.ts | 7 + src/portal/src/app/shared/units/utils.ts | 15 + src/portal/src/i18n/lang/de-de-lang.json | 37 ++ src/portal/src/i18n/lang/en-us-lang.json | 37 ++ src/portal/src/i18n/lang/es-es-lang.json | 37 ++ src/portal/src/i18n/lang/fr-fr-lang.json | 37 ++ src/portal/src/i18n/lang/ko-kr-lang.json | 37 ++ src/portal/src/i18n/lang/pt-br-lang.json | 37 ++ src/portal/src/i18n/lang/tr-tr-lang.json | 37 ++ src/portal/src/i18n/lang/zh-cn-lang.json | 37 ++ src/portal/src/i18n/lang/zh-tw-lang.json | 37 ++ src/server/v2.0/handler/scan.go | 2 +- src/server/v2.0/handler/scan_test.go | 9 +- src/testing/controller/scan/controller.go | 10 +- tests/apitests/python/library/scan_stop.py | 4 +- tests/ci/api_run.sh | 2 +- 46 files changed, 2357 insertions(+), 91 deletions(-) create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-overview.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan-component.html create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.html create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.scss create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.spec.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/sbom-scanning/scanning.scss diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 8c23e29f3..ce4210ec8 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -1206,6 +1206,12 @@ paths: - $ref: '#/parameters/projectName' - $ref: '#/parameters/repositoryName' - $ref: '#/parameters/reference' + - name: scanType + in: body + required: true + schema: + $ref: '#/definitions/ScanType' + description: 'The scan type: Vulnerabilities, SBOM' responses: '202': $ref: '#/responses/202' @@ -9980,3 +9986,11 @@ definitions: items: type: string description: Links of the vulnerability + + ScanType: + type: object + properties: + scan_type: + type: string + description: 'The scan type for the scan request. Two options are currently supported, vulnerability and sbom' + enum: [ vulnerability, sbom ] \ No newline at end of file diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index be2b371d4..a57d1f21b 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -340,15 +340,16 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti } // Stop scan job of a given artifact -func (bc *basicController) Stop(ctx context.Context, artifact *ar.Artifact) error { +func (bc *basicController) Stop(ctx context.Context, artifact *ar.Artifact, capType string) error { if artifact == nil { return errors.New("nil artifact to stop scan") } - query := q.New(q.KeyWords{"extra_attrs.artifact.digest": artifact.Digest}) + query := q.New(q.KeyWords{"vendor_type": job.ImageScanJobVendorType, "extra_attrs.artifact.digest": artifact.Digest, "extra_attrs.enabled_capabilities.type": capType}) executions, err := bc.execMgr.List(ctx, query) if err != nil { return err } + if len(executions) == 0 { message := fmt.Sprintf("no scan job for artifact digest=%v", artifact.Digest) return errors.BadRequestError(nil).WithMessage(message) diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index ced106577..9d9f2c334 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -381,7 +381,7 @@ func (suite *ControllerTestSuite) TestScanControllerScan() { func (suite *ControllerTestSuite) TestScanControllerStop() { { // artifact not provieded - suite.Require().Error(suite.c.Stop(context.TODO(), nil)) + suite.Require().Error(suite.c.Stop(context.TODO(), nil, "vulnerability")) } { @@ -393,7 +393,7 @@ func (suite *ControllerTestSuite) TestScanControllerStop() { ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) - suite.Require().NoError(suite.c.Stop(ctx, suite.artifact)) + suite.Require().NoError(suite.c.Stop(ctx, suite.artifact, "vulnerability")) } { @@ -403,7 +403,7 @@ func (suite *ControllerTestSuite) TestScanControllerStop() { ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) - suite.Require().Error(suite.c.Stop(ctx, suite.artifact)) + suite.Require().Error(suite.c.Stop(ctx, suite.artifact, "vulnerability")) } { @@ -412,7 +412,7 @@ func (suite *ControllerTestSuite) TestScanControllerStop() { ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) - suite.Require().Error(suite.c.Stop(ctx, suite.artifact)) + suite.Require().Error(suite.c.Stop(ctx, suite.artifact, "vulnerability")) } } diff --git a/src/controller/scan/controller.go b/src/controller/scan/controller.go index b890ff7d3..625e8f86f 100644 --- a/src/controller/scan/controller.go +++ b/src/controller/scan/controller.go @@ -55,10 +55,11 @@ type Controller interface { // Arguments: // ctx context.Context : the context for this method // artifact *artifact.Artifact : the artifact whose scan job to be stopped + // capType string : the capability type of the scanner, vulnerability or SBOM. // // Returns: // error : non nil error if any errors occurred - Stop(ctx context.Context, artifact *artifact.Artifact) error + Stop(ctx context.Context, artifact *artifact.Artifact, capType string) error // GetReport gets the reports for the given artifact identified by the digest // diff --git a/src/pkg/task/dao/execution.go b/src/pkg/task/dao/execution.go index cd26e792e..8cd66e75a 100644 --- a/src/pkg/task/dao/execution.go +++ b/src/pkg/task/dao/execution.go @@ -343,6 +343,12 @@ func (e *executionDAO) refreshStatus(ctx context.Context, id int64) (bool, strin return status != execution.Status, status, false, err } +type jsonbStru struct { + keyPrefix string + key string + value interface{} +} + func (e *executionDAO) querySetter(ctx context.Context, query *q.Query) (orm.QuerySeter, error) { qs, err := orm.QuerySetter(ctx, &Execution{}, query) if err != nil { @@ -352,39 +358,32 @@ func (e *executionDAO) querySetter(ctx context.Context, query *q.Query) (orm.Que // append the filter for "extra attrs" if query != nil && len(query.Keywords) > 0 { var ( - key string - keyPrefix string - value interface{} + jsonbStrus []jsonbStru + args []interface{} ) - for key, value = range query.Keywords { - if strings.HasPrefix(key, "ExtraAttrs.") { - keyPrefix = "ExtraAttrs." - break + + for key, value := range query.Keywords { + if strings.HasPrefix(key, "ExtraAttrs.") && key != "ExtraAttrs." { + jsonbStrus = append(jsonbStrus, jsonbStru{ + keyPrefix: "ExtraAttrs.", + key: key, + value: value, + }) } - if strings.HasPrefix(key, "extra_attrs.") { - keyPrefix = "extra_attrs." - break + if strings.HasPrefix(key, "extra_attrs.") && key != "extra_attrs." { + jsonbStrus = append(jsonbStrus, jsonbStru{ + keyPrefix: "extra_attrs.", + key: key, + value: value, + }) } } - if len(keyPrefix) == 0 || keyPrefix == key { + if len(jsonbStrus) == 0 { return qs, nil } - // key with keyPrefix supports multi-level query operator on PostgreSQL JSON data - // examples: - // key = extra_attrs.id, - // ==> sql = "select id from execution where extra_attrs->>?=?", args = {id, value} - // key = extra_attrs.artifact.digest - // ==> sql = "select id from execution where extra_attrs->?->>?=?", args = {artifact, id, value} - // key = extra_attrs.a.b.c - // ==> sql = "select id from execution where extra_attrs->?->?->>?=?", args = {a, b, c, value} - keys := strings.Split(strings.TrimPrefix(key, keyPrefix), ".") - var args []interface{} - for _, item := range keys { - args = append(args, item) - } - args = append(args, value) - inClause, err := orm.CreateInClause(ctx, buildInClauseSQLForExtraAttrs(keys), args...) + idSQL, args := buildInClauseSQLForExtraAttrs(jsonbStrus) + inClause, err := orm.CreateInClause(ctx, idSQL, args...) if err != nil { return nil, err } @@ -395,23 +394,60 @@ func (e *executionDAO) querySetter(ctx context.Context, query *q.Query) (orm.Que } // Param keys is strings.Split() after trim "extra_attrs."/"ExtraAttrs." prefix -func buildInClauseSQLForExtraAttrs(keys []string) string { - switch len(keys) { - case 0: - // won't fall into this case, as the if condition on "keyPrefix == key" - // act as a place holder to ensure "default" is equivalent to "len(keys) >= 2" - return "" - case 1: - return "select id from execution where extra_attrs->>?=?" - default: - // len(keys) >= 2 - elements := make([]string, len(keys)-1) - for i := range elements { - elements[i] = "?" - } - s := strings.Join(elements, "->") - return fmt.Sprintf("select id from execution where extra_attrs->%s->>?=?", s) +// key with keyPrefix supports multi-level query operator on PostgreSQL JSON data +// examples: +// key = extra_attrs.id, +// +// ==> sql = "select id from execution where extra_attrs->>?=?", args = {id, value} +// +// key = extra_attrs.artifact.digest +// +// ==> sql = "select id from execution where extra_attrs->?->>?=?", args = {artifact, id, value} +// +// key = extra_attrs.a.b.c +// +// ==> sql = "select id from execution where extra_attrs->?->?->>?=?", args = {a, b, c, value} +func buildInClauseSQLForExtraAttrs(jsonbStrus []jsonbStru) (string, []interface{}) { + if len(jsonbStrus) == 0 { + return "", nil } + + var cond string + var args []interface{} + sql := "select id from execution where" + + for i, jsonbStr := range jsonbStrus { + if jsonbStr.key == "" || jsonbStr.value == "" { + return "", nil + } + keys := strings.Split(strings.TrimPrefix(jsonbStr.key, jsonbStr.keyPrefix), ".") + if len(keys) == 1 { + if i == 0 { + cond += "extra_attrs->>?=?" + } else { + cond += " and extra_attrs->>?=?" + } + } + if len(keys) >= 2 { + elements := make([]string, len(keys)-1) + for i := range elements { + elements[i] = "?" + } + s := strings.Join(elements, "->") + if i == 0 { + cond += fmt.Sprintf("extra_attrs->%s->>?=?", s) + } else { + cond += fmt.Sprintf(" and extra_attrs->%s->>?=?", s) + } + } + + for _, item := range keys { + args = append(args, item) + } + args = append(args, jsonbStr.value) + } + + return fmt.Sprintf("%s %s", sql, cond), args } func buildExecStatusOutdateKey(id int64, vendor string) string { diff --git a/src/pkg/task/dao/execution_test.go b/src/pkg/task/dao/execution_test.go index 732286eab..edf711d84 100644 --- a/src/pkg/task/dao/execution_test.go +++ b/src/pkg/task/dao/execution_test.go @@ -395,22 +395,36 @@ func TestExecutionDAOSuite(t *testing.T) { } func Test_buildInClauseSQLForExtraAttrs(t *testing.T) { - type args struct { - keys []string - } tests := []struct { name string - args args + args []jsonbStru want string }{ - {"extra_attrs.", args{[]string{}}, ""}, - {"extra_attrs.id", args{[]string{"id"}}, "select id from execution where extra_attrs->>?=?"}, - {"extra_attrs.artifact.digest", args{[]string{"artifact", "digest"}}, "select id from execution where extra_attrs->?->>?=?"}, - {"extra_attrs.a.b.c", args{[]string{"a", "b", "c"}}, "select id from execution where extra_attrs->?->?->>?=?"}, + {"extra_attrs.", []jsonbStru{}, ""}, + {"extra_attrs.", []jsonbStru{{}}, ""}, + {"extra_attrs.id", []jsonbStru{{ + keyPrefix: "extra_attrs.", + key: "extra_attrs.id", + value: "1", + }}, "select id from execution where extra_attrs->>?=?"}, + {"extra_attrs.artifact.digest", []jsonbStru{{ + keyPrefix: "extra_attrs.", + key: "extra_attrs.artifact.digest", + value: "sha256:1234", + }}, "select id from execution where extra_attrs->?->>?=?"}, + {"extra_attrs.a.b.c", []jsonbStru{{ + keyPrefix: "extra_attrs.", + key: "extra_attrs.a.b.c", + value: "test_value_1", + }, { + keyPrefix: "extra_attrs.", + key: "extra_attrs.d.e", + value: "test_value_2", + }}, "select id from execution where extra_attrs->?->?->>?=? and extra_attrs->?->>?=?"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := buildInClauseSQLForExtraAttrs(tt.args.keys); got != tt.want { + if got, _ := buildInClauseSQLForExtraAttrs(tt.args); got != tt.want { t.Errorf("buildInClauseSQLForExtraAttrs() = %v, want %v", got, tt.want) } }) diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/models.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/models.ts index 84bf9bf09..dc7ec0a45 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/models.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/models.ts @@ -18,4 +18,5 @@ export enum ADDITIONS { SUMMARY = 'readme.md', VALUES = 'values.yaml', DEPENDENCIES = 'dependencies', + SBOMS = 'sboms', } diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.spec.ts index 6085c7cd4..2d01b1f34 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.spec.ts @@ -1,12 +1,56 @@ import { inject, TestBed } from '@angular/core/testing'; import { ArtifactListPageService } from './artifact-list-page.service'; import { SharedTestingModule } from '../../../../../shared/shared.module'; +import { + ScanningResultService, + UserPermissionService, +} from 'src/app/shared/services'; +import { of } from 'rxjs'; +import { ClrLoadingState } from '@clr/angular'; describe('ArtifactListPageService', () => { + const FakedScanningResultService = { + getProjectScanner: () => + of({ + access_credential: '', + adapter: 'Trivy', + auth: '', + capabilities: { + support_sbom: true, + support_vulnerability: true, + }, + create_time: '2024-03-06T09:29:43.789Z', + description: 'The Trivy scanner adapter', + disabled: false, + health: 'healthy', + is_default: true, + name: 'Trivy', + skip_certVerify: false, + update_time: '2024-03-06T09:29:43.789Z', + url: 'http://trivy-adapter:8080', + use_internal_addr: true, + uuid: '10c68b62-db9c-11ee-9c72-0242ac130009', + vendor: 'Aqua Security', + version: 'v0.47.0', + }), + }; + const FakedUserPermissionService = { + hasProjectPermissions: () => of([true, true, true, true, true]), + }; beforeEach(() => { TestBed.configureTestingModule({ imports: [SharedTestingModule], - providers: [ArtifactListPageService], + providers: [ + ArtifactListPageService, + { + provide: ScanningResultService, + useValue: FakedScanningResultService, + }, + { + provide: UserPermissionService, + useValue: FakedUserPermissionService, + }, + ], }); }); @@ -16,4 +60,51 @@ describe('ArtifactListPageService', () => { expect(service).toBeTruthy(); } )); + it('Test ArtifactListPageService Permissions validation ', inject( + [ArtifactListPageService], + (service: ArtifactListPageService) => { + service.init(3); + expect(service.hasSbomPermission()).toBeTruthy(); + expect(service.hasAddLabelImagePermission()).toBeTruthy(); + expect(service.hasRetagImagePermission()).toBeTruthy(); + expect(service.hasDeleteImagePermission()).toBeTruthy(); + expect(service.hasScanImagePermission()).toBeTruthy(); + expect(service.hasScannerSupportVulnerability()).toBeTruthy(); + expect(service.hasScannerSupportSBOM()).toBeTruthy(); + } + )); + it('Test ArtifactListPageService updateStates', inject( + [ArtifactListPageService], + (service: ArtifactListPageService) => { + service.init(3); + expect(service.hasEnabledScanner()).toBeTruthy(); + expect(service.getScanBtnState()).toBe(ClrLoadingState.SUCCESS); + expect(service.getSbomBtnState()).toBe(ClrLoadingState.SUCCESS); + service.updateStates( + false, + ClrLoadingState.ERROR, + ClrLoadingState.ERROR + ); + expect(service.hasEnabledScanner()).toBeFalsy(); + expect(service.getScanBtnState()).toBe(ClrLoadingState.ERROR); + expect(service.getSbomBtnState()).toBe(ClrLoadingState.ERROR); + } + )); + it('Test ArtifactListPageService updateCapabilities ', inject( + [ArtifactListPageService], + (service: ArtifactListPageService) => { + service.updateCapabilities({ + support_vulnerability: true, + support_sbom: true, + }); + expect(service.hasScannerSupportVulnerability()).toBeTruthy(); + expect(service.hasScannerSupportSBOM()).toBeTruthy(); + service.updateCapabilities({ + support_vulnerability: false, + support_sbom: false, + }); + expect(service.hasScannerSupportVulnerability()).toBeFalsy(); + expect(service.hasScannerSupportSBOM()).toBeFalsy(); + } + )); }); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts index 701d4c453..a858938dd 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts @@ -10,11 +10,15 @@ import { ErrorHandler } from '../../../../../shared/units/error-handler'; @Injectable() export class ArtifactListPageService { private _scanBtnState: ClrLoadingState; + private _sbomBtnState: ClrLoadingState; private _hasEnabledScanner: boolean = false; + private _hasScannerSupportVulnerability: boolean = false; + private _hasScannerSupportSBOM: boolean = false; private _hasAddLabelImagePermission: boolean = false; private _hasRetagImagePermission: boolean = false; private _hasDeleteImagePermission: boolean = false; private _hasScanImagePermission: boolean = false; + private _hasSbomPermission: boolean = false; constructor( private scanningService: ScanningResultService, @@ -26,6 +30,10 @@ export class ArtifactListPageService { return this._scanBtnState; } + getSbomBtnState(): ClrLoadingState { + return this._sbomBtnState; + } + hasEnabledScanner(): boolean { return this._hasEnabledScanner; } @@ -46,14 +54,53 @@ export class ArtifactListPageService { return this._hasScanImagePermission; } + hasSbomPermission(): boolean { + return this._hasSbomPermission; + } + + hasScannerSupportVulnerability(): boolean { + return this._hasScannerSupportVulnerability; + } + + hasScannerSupportSBOM(): boolean { + return this._hasScannerSupportSBOM; + } + init(projectId: number) { this._getProjectScanner(projectId); this._getPermissionRule(projectId); } + updateStates( + enabledScanner: boolean, + scanState?: ClrLoadingState, + sbomState?: ClrLoadingState + ) { + if (scanState) { + this._scanBtnState = scanState; + } + if (sbomState) { + this._sbomBtnState = sbomState; + } + this._hasEnabledScanner = enabledScanner; + } + + updateCapabilities(capabilities?: any) { + if (capabilities) { + if (capabilities?.support_vulnerability !== undefined) { + this._hasScannerSupportVulnerability = + capabilities.support_vulnerability; + } + if (capabilities?.support_sbom !== undefined) { + this._hasScannerSupportSBOM = capabilities.support_sbom; + } + } + } + private _getProjectScanner(projectId: number): void { this._hasEnabledScanner = false; this._scanBtnState = ClrLoadingState.LOADING; + this._sbomBtnState = ClrLoadingState.LOADING; this.scanningService.getProjectScanner(projectId).subscribe( response => { if ( @@ -62,14 +109,28 @@ export class ArtifactListPageService { !response.disabled && response.health === 'healthy' ) { - this._scanBtnState = ClrLoadingState.SUCCESS; - this._hasEnabledScanner = true; + this.updateStates( + true, + ClrLoadingState.SUCCESS, + ClrLoadingState.SUCCESS + ); + if (response?.capabilities) { + this.updateCapabilities(response?.capabilities); + } } else { - this._scanBtnState = ClrLoadingState.ERROR; + this.updateStates( + false, + ClrLoadingState.ERROR, + ClrLoadingState.ERROR + ); } }, error => { - this._scanBtnState = ClrLoadingState.ERROR; + this.updateStates( + false, + ClrLoadingState.ERROR, + ClrLoadingState.ERROR + ); } ); } @@ -94,6 +155,11 @@ export class ArtifactListPageService { action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE .CREATE, }, + { + resource: USERSTATICPERMISSION.REPOSITORY_TAG_SBOM_JOB.KEY, + action: USERSTATICPERMISSION.REPOSITORY_TAG_SBOM_JOB.VALUE + .CREATE, + }, ]; this.userPermissionService .hasProjectPermissions(projectId, permissions) @@ -103,6 +169,9 @@ export class ArtifactListPageService { this._hasRetagImagePermission = results[1]; this._hasDeleteImagePermission = results[2]; this._hasScanImagePermission = results[3]; + this._hasSbomPermission = results?.[4] ?? false; + // TODO need to remove the static code + this._hasSbomPermission = true; }, error => this.errorHandlerService.error(error) ); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html index 6258f7a7d..1001da144 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html @@ -42,6 +42,34 @@   {{ 'VULNERABILITY.STOP_NOW' | translate }} + + - + + {{ 'REPOSITORY.SBOM' | translate }} + + + + {{ 'ARTIFACT.ANNOTATION' | translate }} - + {{ 'REPOSITORY.LABELS' | translate }} - + {{ 'REPOSITORY.PUSH_TIME' | translate }} - + {{ 'REPOSITORY.PULL_TIME' | translate }} @@ -389,6 +423,26 @@ + +
+ + {{ 'ARTIFACT.SBOM_UNSUPPORTED' | translate }} + + + +
+
diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss index b90fad677..23c95498d 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.scss @@ -161,6 +161,10 @@ width: 11rem !important; } +.sbom-column { + width: 6rem !important; +} + .annotations-column { width: 5rem !important; } diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts index 9eef1097e..27010f065 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts @@ -13,7 +13,7 @@ import { ScanningResultDefaultService, ScanningResultService, } from '../../../../../../../shared/services'; -import { ArtifactFront as Artifact } from '../../../artifact'; +import { ArtifactFront as Artifact, ArtifactFront } from '../../../artifact'; import { ErrorHandler } from '../../../../../../../shared/units/error-handler'; import { OperationService } from '../../../../../../../shared/components/operation/operation.service'; import { ArtifactService as NewArtifactService } from '../../../../../../../../../ng-swagger-gen/services/artifact.service'; @@ -24,6 +24,10 @@ import { ArtifactListPageService } from '../../artifact-list-page.service'; import { ClrLoadingState } from '@clr/angular'; import { Accessory } from 'ng-swagger-gen/models/accessory'; import { ArtifactModule } from '../../../artifact.module'; +import { + SBOM_SCAN_STATUS, + VULNERABILITY_SCAN_STATUS, +} from 'src/app/shared/units/utils'; describe('ArtifactListTabComponent', () => { let comp: ArtifactListTabComponent; @@ -171,6 +175,16 @@ describe('ArtifactListTabComponent', () => { pull_time: '0001-01-01T00:00:00Z', }, ]; + const mockAccessory = { + id: 1, + artifact_id: 2, + subject_artifact_id: 3, + subject_artifact_digest: 'fakeDigest', + subject_artifact_repo: 'test', + size: 120, + digest: 'fakeDigest', + type: 'test', + }; const mockErrorHandler = { error: () => {}, }; @@ -236,9 +250,18 @@ describe('ArtifactListTabComponent', () => { getScanBtnState(): ClrLoadingState { return ClrLoadingState.DEFAULT; }, + getSbomBtnState(): ClrLoadingState { + return ClrLoadingState.DEFAULT; + }, hasEnabledScanner(): boolean { return true; }, + hasSbomPermission(): boolean { + return true; + }, + hasScannerSupportSBOM(): boolean { + return true; + }, hasAddLabelImagePermission(): boolean { return true; }, @@ -353,6 +376,27 @@ describe('ArtifactListTabComponent', () => { fixture.nativeElement.querySelector('.confirmation-title') ).toBeTruthy(); }); + it('Generate SBOM button should be disabled', async () => { + await fixture.whenStable(); + comp.selectedRow = [mockArtifacts[1]]; + await stepOpenAction(fixture, comp); + const generatedButton = + fixture.nativeElement.querySelector('#generate-sbom-btn'); + fixture.detectChanges(); + await fixture.whenStable(); + expect(generatedButton.disabled).toBeTruthy(); + }); + it('Stop SBOM button should be disabled', async () => { + await fixture.whenStable(); + comp.selectedRow = [mockArtifacts[1]]; + await stepOpenAction(fixture, comp); + const stopButton = + fixture.nativeElement.querySelector('#stop-sbom-btn'); + fixture.detectChanges(); + await fixture.whenStable().then(() => { + expect(stopButton.disabled).toBeTruthy(); + }); + }); it('the length of hide array should equal to the number of column', async () => { comp.loading = false; fixture.detectChanges(); @@ -360,6 +404,93 @@ describe('ArtifactListTabComponent', () => { const cols = fixture.nativeElement.querySelectorAll('.datagrid-column'); expect(cols.length).toEqual(comp.hiddenArray.length); }); + + it('Test isEllipsisActive', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable().then(() => { + expect( + comp.isEllipsisActive(document.createElement('span')) + ).toBeFalsy(); + }); + }); + it('Test deleteAccessory', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + comp.deleteAccessory(mockAccessory); + fixture.detectChanges(); + await fixture.whenStable().then(() => { + expect( + fixture.nativeElement.querySelector('.confirmation-content') + ).toBeTruthy(); + }); + }); + it('Test scanNow', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + comp.selectedRow = mockArtifacts.slice(0, 1); + comp.scanNow(); + expect(comp.onScanArtifactsLength).toBe(1); + }); + it('Test stopNow', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + comp.selectedRow = mockArtifacts.slice(0, 1); + comp.stopNow(); + expect(comp.onStopScanArtifactsLength).toBe(1); + }); + it('Test stopSbom', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + comp.selectedRow = mockArtifacts.slice(0, 1); + comp.stopSbom(); + expect(comp.onStopSbomArtifactsLength).toBe(1); + }); + it('Test tagsString and isRunningState and canStopSbom and canStopScan', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable(); + expect(comp.tagsString([])).toBeNull(); + expect( + comp.isRunningState(VULNERABILITY_SCAN_STATUS.RUNNING) + ).toBeTruthy(); + expect( + comp.isRunningState(VULNERABILITY_SCAN_STATUS.ERROR) + ).toBeFalsy(); + expect(comp.canStopSbom()).toBeFalsy(); + expect(comp.canStopScan()).toBeFalsy(); + }); + it('Test status and handleScanOverview', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable(); + expect(comp.scanStatus(mockArtifacts[0])).toBe( + VULNERABILITY_SCAN_STATUS.ERROR + ); + expect(comp.sbomStatus(null)).toBe(SBOM_SCAN_STATUS.NOT_GENERATED_SBOM); + expect(comp.sbomStatus(mockArtifacts[0])).toBe( + SBOM_SCAN_STATUS.NOT_GENERATED_SBOM + ); + expect(comp.handleScanOverview(mockArtifacts[0])).not.toBeNull(); + }); + it('Test utils', async () => { + fixture = TestBed.createComponent(ArtifactListTabComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable(); + expect(comp.selectedRowHasSbom()).toBeFalsy(); + expect(comp.selectedRowHasVul()).toBeFalsy(); + expect(comp.canScanNow()).toBeFalsy(); + expect(comp.hasEnabledSbom()).toBeTruthy(); + expect(comp.canAddLabel()).toBeFalsy(); + }); }); async function stepOpenAction(fixture, comp) { diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts index 5d4ffad46..4675031dc 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts @@ -37,6 +37,7 @@ import { setHiddenArrayToLocalStorage, setPageSizeToLocalStorage, VULNERABILITY_SCAN_STATUS, + SBOM_SCAN_STATUS, } from '../../../../../../../shared/units/utils'; import { ErrorHandler } from '../../../../../../../shared/units/error-handler'; import { ArtifactService } from '../../../artifact.service'; @@ -76,7 +77,7 @@ import { EventService, HarborEvent, } from '../../../../../../../services/event-service/event.service'; -import { AppConfigService } from 'src/app/services/app-config.service'; +import { AppConfigService } from '../../../../../../../services/app-config.service'; import { ArtifactListPageService } from '../../artifact-list-page.service'; import { ACCESSORY_PAGE_SIZE } from './sub-accessories/sub-accessories.component'; import { Accessory } from 'ng-swagger-gen/models/accessory'; @@ -141,28 +142,60 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { get hasScanImagePermission(): boolean { return this.artifactListPageService.hasScanImagePermission(); } + get hasSbomPermission(): boolean { + return this.artifactListPageService.hasSbomPermission(); + } get hasEnabledScanner(): boolean { return this.artifactListPageService.hasEnabledScanner(); } + get hasScannerSupportVulnerability(): boolean { + return this.artifactListPageService.hasScannerSupportVulnerability(); + } + get hasScannerSupportSBOM(): boolean { + return this.artifactListPageService.hasScannerSupportSBOM(); + } get scanBtnState(): ClrLoadingState { return this.artifactListPageService.getScanBtnState(); } + get generateSbomBtnState(): ClrLoadingState { + return this.artifactListPageService.getSbomBtnState(); + } onSendingScanCommand: boolean; onSendingStopScanCommand: boolean = false; onStopScanArtifactsLength: number = 0; scanStoppedArtifactLength: number = 0; + + onSendingSbomCommand: boolean; + onSendingStopSbomCommand: boolean = false; + onStopSbomArtifactsLength: number = 0; + sbomStoppedArtifactLength: number = 0; + artifactDigest: string; depth: string; // could Pagination filter filters: string[]; scanFinishedArtifactLength: number = 0; onScanArtifactsLength: number = 0; + sbomFinishedArtifactLength: number = 0; + onSbomArtifactsLength: number = 0; stopBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; updateArtifactSub: Subscription; hiddenArray: boolean[] = getHiddenArrayFromLocalStorage( PageSizeMapKeys.ARTIFACT_LIST_TAB_COMPONENT, - [false, false, false, false, false, false, true, false, false, false] + [ + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + ] ); deleteAccessorySub: Subscription; copyDigestSub: Subscription; @@ -203,7 +236,8 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } } ngOnInit() { - this.registryUrl = this.appConfigService.getConfig().registry_url; + const appConfig = this.appConfigService.getConfig(); + this.registryUrl = appConfig.registry_url; this.initRouterData(); if (!this.updateArtifactSub) { this.updateArtifactSub = this.eventService.subscribe( @@ -250,7 +284,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { this.copyDigestSub.unsubscribe(); this.copyDigestSub = null; } - this.datagrid['columnsService']?.columns?.forEach((item, index) => { + this.datagrid?.['columnsService']?.columns?.forEach((item, index) => { if (this.depth) { this.hiddenArray[index] = !!item?._value?.hidden; } else { @@ -326,6 +360,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { withImmutableStatus: true, withLabel: true, withScanOverview: true, + // withSbomOverview: true, withTag: false, XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, withAccessory: false, @@ -350,6 +385,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { withImmutableStatus: true, withLabel: true, withScanOverview: true, + // withSbomOverview: true, withTag: false, XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, @@ -381,7 +417,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { }); this.getArtifactTagsAsync(this.artifactList); this.getAccessoriesAsync(this.artifactList); - this.checkCosignAsync(this.artifactList); + this.checkCosignAndSbomAsync(this.artifactList); this.getIconsFromBackEnd(); }, error => { @@ -420,7 +456,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { this.artifactList = res.body; this.getArtifactTagsAsync(this.artifactList); this.getAccessoriesAsync(this.artifactList); - this.checkCosignAsync(this.artifactList); + this.checkCosignAndSbomAsync(this.artifactList); this.getIconsFromBackEnd(); }, error => { @@ -519,6 +555,14 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { return formatSize(tagSize); } + hasEnabledSbom(): boolean { + return ( + this.hasScannerSupportSBOM && + this.hasEnabledScanner && + this.hasSbomPermission + ); + } + retag() { if (this.selectedRow && this.selectedRow.length && !this.depth) { this.copyArtifactComponent.retag(this.selectedRow[0].digest); @@ -714,6 +758,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } } + // Get sbom status + sbomStatus(artifact: Artifact): string { + if (artifact) { + let so = (artifact).sbom_overview; + if (so && so.scan_status) { + return so.scan_status; + } + } + return SBOM_SCAN_STATUS.NOT_GENERATED_SBOM; + } + // Get vulnerability scanning status scanStatus(artifact: Artifact): string { if (artifact) { @@ -771,6 +826,10 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { ); } + selectedRowHasSbom(): boolean { + return !!(this.selectedRow && this.selectedRow[0]); + } + hasVul(artifact: Artifact): boolean { return !!( artifact && @@ -779,6 +838,14 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { ); } + hasSbom(artifact: Artifact): boolean { + return !!( + artifact && + artifact.addition_links && + artifact.addition_links[ADDITIONS.SBOMS] + ); + } + submitFinish(e: boolean) { this.scanFinishedArtifactLength += 1; // all selected scan action has started @@ -794,9 +861,27 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { this.onSendingScanCommand = e; } } + + submitSbomFinish(e: boolean) { + this.sbomFinishedArtifactLength += 1; + // all selected scan action has started + if (this.sbomFinishedArtifactLength === this.onSbomArtifactsLength) { + this.onSendingSbomCommand = e; + } + } + + submitSbomStopFinish(e: boolean) { + this.sbomStoppedArtifactLength += 1; + // all selected scan action has stopped + if (this.sbomStoppedArtifactLength === this.onStopSbomArtifactsLength) { + this.onSendingSbomCommand = e; + } + } + handleScanOverview(scanOverview: any): any { if (scanOverview) { - return Object.values(scanOverview)[0]; + const keys = Object.keys(scanOverview) ?? []; + return keys.length > 0 ? scanOverview[keys[0]] : null; } return null; } @@ -857,6 +942,11 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } } + // when finished, remove it from selectedRow + sbomFinished(artifact: Artifact) { + this.scanFinished(artifact); + } + getIconsFromBackEnd() { if (this.artifactList && this.artifactList.length) { this.artifactService.getIconsFromBackEnd(this.artifactList); @@ -929,11 +1019,18 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { }); } } - checkCosignAsync(artifacts: ArtifactFront[]) { + + checkCosignAndSbomAsync(artifacts: ArtifactFront[]) { if (artifacts) { if (artifacts.length) { artifacts.forEach(item => { item.signed = CHECKING; + // const sbomOverview = item?.sbom_overview; + // item.sbomDigest = sbomOverview?.sbom_digest; + // let queryTypes = `${AccessoryType.COSIGN} ${AccessoryType.NOTATION}`; + // if (!item.sbomDigest) { + // queryTypes = `${queryTypes} ${AccessoryType.SBOM}`; + // } this.newArtifactService .listAccessories({ projectName: this.projectName, @@ -979,6 +1076,24 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { return false; } + // return true if all selected rows are in "running" state + canStopSbom(): boolean { + if (this.onSendingStopSbomCommand) { + return false; + } + if (this.selectedRow && this.selectedRow.length) { + let flag: boolean = true; + this.selectedRow.forEach(item => { + const st: string = this.sbomStatus(item); + if (!this.isRunningState(st)) { + flag = false; + } + }); + return flag; + } + return false; + } + isRunningState(state: string): boolean { return ( state === VULNERABILITY_SCAN_STATUS.RUNNING || @@ -1001,6 +1116,22 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { }); } } + + stopSbom() { + if (this.selectedRow && this.selectedRow.length) { + this.sbomStoppedArtifactLength = 0; + this.onStopSbomArtifactsLength = this.selectedRow.length; + this.onSendingStopSbomCommand = true; + this.selectedRow.forEach((data: any) => { + let digest = data.digest; + this.eventService.publish( + HarborEvent.STOP_SBOM_ARTIFACT, + this.repoName + '/' + digest + ); + }); + } + } + tagsString(tags: Tag[]): string { if (tags?.length) { const arr: string[] = []; diff --git a/src/portal/src/app/base/project/repository/artifact/artifact.module.ts b/src/portal/src/app/base/project/repository/artifact/artifact.module.ts index bd4f49961..4ff6da2d4 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact.module.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact.module.ts @@ -15,6 +15,7 @@ import { ArtifactVulnerabilitiesComponent } from './artifact-additions/artifact- import { ArtifactDefaultService, ArtifactService } from './artifact.service'; import { ArtifactDetailRoutingResolverService } from '../../../../services/routing-resolvers/artifact-detail-routing-resolver.service'; import { ResultBarChartComponent } from './vulnerability-scanning/result-bar-chart.component'; +import { ResultSbomComponent } from './sbom-scanning/sbom-scan.component'; import { ResultTipHistogramComponent } from './vulnerability-scanning/result-tip-histogram/result-tip-histogram.component'; import { HistogramChartComponent } from './vulnerability-scanning/histogram-chart/histogram-chart.component'; import { ArtifactInfoComponent } from './artifact-list-page/artifact-list/artifact-info/artifact-info.component'; @@ -24,6 +25,7 @@ import { CopyArtifactComponent } from './artifact-list-page/artifact-list/artifa import { CopyDigestComponent } from './artifact-list-page/artifact-list/artifact-list-tab/copy-digest/copy-digest.component'; import { ArtifactFilterComponent } from './artifact-list-page/artifact-list/artifact-list-tab/artifact-filter/artifact-filter.component'; import { PullCommandComponent } from './artifact-list-page/artifact-list/artifact-list-tab/pull-command/pull-command.component'; +import { SbomTipHistogramComponent } from './sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component'; const routes: Routes = [ { @@ -80,6 +82,8 @@ const routes: Routes = [ BuildHistoryComponent, ArtifactVulnerabilitiesComponent, ResultBarChartComponent, + ResultSbomComponent, + SbomTipHistogramComponent, ResultTipHistogramComponent, HistogramChartComponent, ArtifactInfoComponent, diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-overview.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-overview.ts new file mode 100644 index 000000000..753a32a20 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-overview.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ + +import { Scanner } from 'ng-swagger-gen/models'; + +/** + * The generate SBOM overview information + */ +export interface SBOMOverview { + /** + * id of the native sbom report + */ + report_id?: string; + + /** + * The start time of the scan process that generating report + */ + start_time?: string; + + /** + * The end time of the scan process that generating report + */ + end_time?: string; + + /** + * The status of the generate SBOM process + */ + scan_status?: string; + + /** + * The digest of the generated SBOM accessory + */ + sbom_digest?: string; + + /** + * The seconds spent for generating the report + */ + duration?: number; + scanner?: Scanner; +} diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan-component.html b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan-component.html new file mode 100644 index 000000000..1b537edd5 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan-component.html @@ -0,0 +1,38 @@ +
+
+ {{ + 'SBOM.STATE.QUEUED' | translate + }} +
+ +
+
{{ 'SBOM.STATE.SCANNING' | translate }}
+
+
+
+ +
+
+ {{ + 'SBOM.STATE.STOPPED' | translate + }} +
+
+ {{ + 'SBOM.STATE.OTHER_STATUS' | translate + }} +
+
diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts new file mode 100644 index 000000000..af74ddde8 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts @@ -0,0 +1,245 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ResultSbomComponent } from './sbom-scan.component'; +import { + ScanningResultDefaultService, + ScanningResultService, +} from '../../../../../shared/services'; +import { SBOM_SCAN_STATUS } from '../../../../../shared/units/utils'; +import { SharedTestingModule } from '../../../../../shared/shared.module'; +import { SbomTipHistogramComponent } from './sbom-tip-histogram/sbom-tip-histogram.component'; +import { SBOMOverview } from './sbom-overview'; +import { of, timer } from 'rxjs'; +import { ArtifactService, ScanService } from 'ng-swagger-gen/services'; +import { Artifact } from 'ng-swagger-gen/models'; + +describe('ResultSbomComponent (inline template)', () => { + let component: ResultSbomComponent; + let fixture: ComponentFixture; + let mockData: SBOMOverview = { + scan_status: SBOM_SCAN_STATUS.SUCCESS, + end_time: new Date().toUTCString(), + }; + const mockedSbomDigest = + 'sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3'; + const mockedSbomOverview = { + report_id: '12345', + scan_status: 'Error', + scanner: { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }, + }; + const mockedCloneSbomOverview = { + report_id: '12346', + scan_status: 'Pending', + scanner: { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }, + }; + const FakedScanService = { + scanArtifact: () => of({}), + stopScanArtifact: () => of({}), + }; + const FakedArtifactService = { + getArtifact: () => + of({ + accessories: null, + addition_links: { + build_history: { + absolute: false, + href: '/api/v2.0/projects/xuel/repositories/ui%252Fserver%252Fconfig-dev/artifacts/sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3/additions/build_history', + }, + vulnerabilities: { + absolute: false, + href: '/api/v2.0/projects/xuel/repositories/ui%252Fserver%252Fconfig-dev/artifacts/sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3/additions/vulnerabilities', + }, + }, + digest: 'sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3', + extra_attrs: { + architecture: 'amd64', + author: '', + config: { + Env: [ + 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + ], + WorkingDir: '/', + }, + created: '2024-01-10T10:05:33.2702206Z', + os: 'linux', + }, + icon: 'sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06', + id: 3, + labels: null, + manifest_media_type: + 'application/vnd.docker.distribution.manifest.v2+json', + media_type: 'application/vnd.docker.container.image.v1+json', + project_id: 3, + pull_time: '2024-04-02T01:50:58.332Z', + push_time: '2024-03-06T09:47:08.163Z', + references: null, + repository_id: 2, + sbom_overview: { + duration: 2, + end_time: '2024-04-02T01:50:59.406Z', + sbom_digest: + 'sha256:8cca43ea666e0e7990c2433e3b185313e6ba303cc7a3124bb767823c79fb74a6', + scan_status: 'Success', + start_time: '2024-04-02T01:50:57.176Z', + }, + size: 3957, + tags: null, + type: 'IMAGE', + }), + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SharedTestingModule], + declarations: [ResultSbomComponent, SbomTipHistogramComponent], + providers: [ + { + provide: ScanningResultService, + useValue: ScanningResultDefaultService, + }, + { + provide: ScanService, + useValue: FakedScanService, + }, + { + provide: ArtifactService, + useValue: FakedArtifactService, + }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ResultSbomComponent); + component = fixture.componentInstance; + component.repoName = 'mockRepo'; + component.artifactDigest = mockedSbomDigest; + component.sbomDigest = mockedSbomDigest; + component.sbomOverview = mockData; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); + it('should show "scan stopped" if status is STOPPED', () => { + component.sbomOverview.scan_status = SBOM_SCAN_STATUS.STOPPED; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + let el: HTMLElement = fixture.nativeElement.querySelector('span'); + expect(el).toBeTruthy(); + expect(el.textContent).toEqual('SBOM.STATE.STOPPED'); + }); + }); + + it('should show progress if status is SCANNING', () => { + component.sbomOverview.scan_status = SBOM_SCAN_STATUS.RUNNING; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let el: HTMLElement = + fixture.nativeElement.querySelector('.progress'); + expect(el).toBeTruthy(); + }); + }); + + it('should show QUEUED if status is QUEUED', () => { + component.sbomOverview.scan_status = SBOM_SCAN_STATUS.PENDING; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let el: HTMLElement = + fixture.nativeElement.querySelector('.bar-state'); + expect(el).toBeTruthy(); + let el2: HTMLElement = el.querySelector('span'); + expect(el2).toBeTruthy(); + expect(el2.textContent).toEqual('SBOM.STATE.QUEUED'); + }); + }); + + it('should show summary bar chart if status is COMPLETED', () => { + component.sbomOverview.scan_status = SBOM_SCAN_STATUS.SUCCESS; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + let el: HTMLElement = fixture.nativeElement.querySelector('a'); + expect(el).not.toBeNull(); + }); + }); + it('Test ResultSbomComponent getScanner', () => { + fixture.detectChanges(); + expect(component.getScanner()).toBeUndefined(); + component.sbomOverview = mockedSbomOverview; + expect(component.getScanner()).toBe(mockedSbomOverview.scanner); + component.projectName = 'test'; + component.repoName = 'ui'; + component.artifactDigest = 'dg'; + expect(component.viewLog()).toBe( + '/api/v2.0/projects/test/repositories/ui/artifacts/dg/scan/12345/log' + ); + component.copyValue(mockedCloneSbomOverview); + expect(component.sbomOverview.report_id).toBe( + mockedCloneSbomOverview.report_id + ); + }); + it('Test ResultSbomComponent status', () => { + component.sbomOverview = mockedSbomOverview; + fixture.detectChanges(); + expect(component.status).toBe(SBOM_SCAN_STATUS.ERROR); + expect(component.completed).toBeFalsy(); + expect(component.queued).toBeFalsy(); + expect(component.generating).toBeFalsy(); + expect(component.stopped).toBeFalsy(); + expect(component.otherStatus).toBeFalsy(); + expect(component.error).toBeTruthy(); + }); + it('Test ResultSbomComponent ngOnDestroy', () => { + component.stateCheckTimer = timer(0, 10000).subscribe(() => {}); + component.ngOnDestroy(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.stateCheckTimer).toBeNull(); + expect(component.generateSbomSubscription).toBeNull(); + expect(component.stopSubscription).toBeNull(); + }); + }); + it('Test ResultSbomComponent generateSbom', () => { + fixture.detectChanges(); + component.generateSbom(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onSubmitting).toBeFalse(); + }); + }); + it('Test ResultSbomComponent stopSbom', () => { + fixture.detectChanges(); + component.stopSbom(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onStopping).toBeFalse(); + }); + }); + it('Test ResultSbomComponent getSbomOverview', () => { + fixture.detectChanges(); + component.getSbomOverview(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.stateCheckTimer).toBeUndefined(); + }); + }); +}); diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts new file mode 100644 index 000000000..4cfced2f8 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts @@ -0,0 +1,327 @@ +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, +} from '@angular/core'; +import { Subscription, timer } from 'rxjs'; +import { finalize } from 'rxjs/operators'; +import { ScannerVo } from '../../../../../shared/services'; +import { ErrorHandler } from '../../../../../shared/units/error-handler'; +import { + clone, + CURRENT_BASE_HREF, + dbEncodeURIComponent, + DEFAULT_SUPPORTED_MIME_TYPES, + SBOM_SCAN_STATUS, +} from '../../../../../shared/units/utils'; +import { ArtifactService } from '../../../../../../../ng-swagger-gen/services/artifact.service'; +import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact'; +import { + EventService, + HarborEvent, +} from '../../../../../services/event-service/event.service'; +import { ScanService } from '../../../../../../../ng-swagger-gen/services/scan.service'; +import { ScanType } from 'ng-swagger-gen/models'; +import { ScanTypes } from '../../../../../shared/entities/shared.const'; +import { SBOMOverview } from './sbom-overview'; +const STATE_CHECK_INTERVAL: number = 3000; // 3s +const RETRY_TIMES: number = 3; + +@Component({ + selector: 'hbr-sbom-bar', + templateUrl: './sbom-scan-component.html', + styleUrls: ['./scanning.scss'], +}) +export class ResultSbomComponent implements OnInit, OnDestroy { + @Input() inputScanner: ScannerVo; + @Input() repoName: string = ''; + @Input() projectName: string = ''; + @Input() projectId: string = ''; + @Input() artifactDigest: string = ''; + @Input() sbomDigest: string = ''; + @Input() sbomOverview: SBOMOverview; + onSubmitting: boolean = false; + onStopping: boolean = false; + retryCounter: number = 0; + stateCheckTimer: Subscription; + generateSbomSubscription: Subscription; + stopSubscription: Subscription; + timerHandler: any; + @Output() + submitFinish: EventEmitter = new EventEmitter(); + // if sending stop scan request is finished, emit to farther component + @Output() + submitStopFinish: EventEmitter = new EventEmitter(); + @Output() + scanFinished: EventEmitter = new EventEmitter(); + + constructor( + private artifactService: ArtifactService, + private scanService: ScanService, + private errorHandler: ErrorHandler, + private eventService: EventService + ) {} + + ngOnInit(): void { + if ( + (this.status === SBOM_SCAN_STATUS.RUNNING || + this.status === SBOM_SCAN_STATUS.PENDING) && + !this.stateCheckTimer + ) { + // Avoid duplicated subscribing + this.stateCheckTimer = timer(0, STATE_CHECK_INTERVAL).subscribe( + () => { + this.getSbomOverview(); + } + ); + } + if (!this.generateSbomSubscription) { + this.generateSbomSubscription = this.eventService.subscribe( + HarborEvent.START_GENERATE_SBOM, + (artifactDigest: string) => { + let myFullTag: string = + this.repoName + '/' + this.artifactDigest; + if (myFullTag === artifactDigest) { + this.generateSbom(); + } + } + ); + } + if (!this.stopSubscription) { + this.stopSubscription = this.eventService.subscribe( + HarborEvent.STOP_SBOM_ARTIFACT, + (artifactDigest: string) => { + let myFullTag: string = + this.repoName + '/' + this.artifactDigest; + if (myFullTag === artifactDigest) { + this.stopSbom(); + } + } + ); + } + } + + ngOnDestroy(): void { + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; + } + if (this.generateSbomSubscription) { + this.generateSbomSubscription.unsubscribe(); + this.generateSbomSubscription = null; + } + if (this.stopSubscription) { + this.stopSubscription.unsubscribe(); + this.stopSubscription = null; + } + } + + // Get vulnerability scanning status + public get status(): string { + if (this.sbomOverview && this.sbomOverview.scan_status) { + return this.sbomOverview.scan_status; + } + return SBOM_SCAN_STATUS.NOT_GENERATED_SBOM; + } + + public get completed(): boolean { + return this.status === SBOM_SCAN_STATUS.SUCCESS; + } + + public get error(): boolean { + return this.status === SBOM_SCAN_STATUS.ERROR; + } + + public get queued(): boolean { + return this.status === SBOM_SCAN_STATUS.PENDING; + } + + public get generating(): boolean { + return this.status === SBOM_SCAN_STATUS.RUNNING; + } + + public get stopped(): boolean { + return this.status === SBOM_SCAN_STATUS.STOPPED; + } + + public get otherStatus(): boolean { + return !( + this.completed || + this.error || + this.queued || + this.generating || + this.stopped + ); + } + + generateSbom(): void { + if (this.onSubmitting) { + // Avoid duplicated submitting + console.error('duplicated submit'); + return; + } + + if (!this.repoName || !this.artifactDigest) { + console.error('bad repository or tag'); + return; + } + + this.onSubmitting = true; + + this.scanService + .scanArtifact({ + projectName: this.projectName, + reference: this.artifactDigest, + repositoryName: dbEncodeURIComponent(this.repoName), + // scanType: { + // scan_type: ScanTypes.SBOM, + // }, + }) + .pipe(finalize(() => this.submitFinish.emit(false))) + .subscribe( + () => { + this.onSubmitting = false; + // Forcely change status to queued after successful submitting + this.sbomOverview = { + scan_status: SBOM_SCAN_STATUS.PENDING, + }; + // Start check status util the job is done + if (!this.stateCheckTimer) { + // Avoid duplicated subscribing + this.stateCheckTimer = timer( + STATE_CHECK_INTERVAL, + STATE_CHECK_INTERVAL + ).subscribe(() => { + this.getSbomOverview(); + }); + } + }, + error => { + this.onSubmitting = false; + if (error && error.error && error.error.code === 409) { + console.error(error.error.message); + } else { + this.errorHandler.error(error); + } + } + ); + } + + getSbomOverview(): void { + if (!this.repoName || !this.artifactDigest) { + return; + } + this.artifactService + .getArtifact({ + projectName: this.projectName, + repositoryName: dbEncodeURIComponent(this.repoName), + reference: this.artifactDigest, + // withSbomOverview: true, + XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, + }) + .subscribe( + (artifact: Artifact) => { + // To keep the same summary reference, use value copy. + // if (artifact.sbom_overview) { + // this.copyValue(artifact.sbom_overview); + // } + if (!this.queued && !this.generating) { + // Scanning should be done + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; + } + this.scanFinished.emit(artifact); + } + this.eventService.publish( + HarborEvent.UPDATE_SBOM_INFO, + artifact + ); + }, + error => { + this.errorHandler.error(error); + this.retryCounter++; + if (this.retryCounter >= RETRY_TIMES) { + // Stop timer + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; + } + this.retryCounter = 0; + } + } + ); + } + + copyValue(newVal: SBOMOverview): void { + if (!this.sbomOverview || !newVal || !newVal.scan_status) { + return; + } + this.sbomOverview = clone(newVal); + } + + viewLog(): string { + return `${CURRENT_BASE_HREF}/projects/${ + this.projectName + }/repositories/${dbEncodeURIComponent(this.repoName)}/artifacts/${ + this.artifactDigest + }/scan/${this.sbomOverview.report_id}/log`; + } + + getScanner(): ScannerVo { + if (this.sbomOverview && this.sbomOverview.scanner) { + return this.sbomOverview.scanner; + } + return this.inputScanner; + } + + stopSbom() { + if (this.onStopping) { + // Avoid duplicated stopping command + console.error('duplicated stopping command for SBOM generation'); + return; + } + if (!this.repoName || !this.artifactDigest) { + console.error('bad repository or artifact'); + return; + } + this.onStopping = true; + + this.scanService + .stopScanArtifact({ + projectName: this.projectName, + reference: this.artifactDigest, + repositoryName: dbEncodeURIComponent(this.repoName), + scanType: { + scan_type: ScanTypes.SBOM, + }, + }) + .pipe( + finalize(() => { + this.submitStopFinish.emit(false); + this.onStopping = false; + }) + ) + .subscribe( + () => { + // Start check status util the job is done + if (!this.stateCheckTimer) { + // Avoid duplicated subscribing + this.stateCheckTimer = timer( + STATE_CHECK_INTERVAL, + STATE_CHECK_INTERVAL + ).subscribe(() => { + this.getSbomOverview(); + }); + } + this.errorHandler.info('SBOM.TRIGGER_STOP_SUCCESS'); + }, + error => { + this.errorHandler.error(error); + } + ); + } +} diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.html b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.html new file mode 100644 index 000000000..0bb28d622 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.html @@ -0,0 +1,78 @@ +
+ +
+ +
+ {{ 'SBOM.NO_SBOM' | translate }} +
+
+ +
+
+
+ {{ 'SBOM.PACKAGES' | translate }} +
+
+ + {{ 'SBOM.NO_SBOM' | translate }} + +
+
+
+ {{ + 'SCANNER.SCANNED_BY' | translate + }} + {{ getScannerInfo() }} +
+
+ {{ + 'SCANNER.DURATION' | translate + }} + {{ duration() }} +
+
+ {{ 'SBOM.CHART.SCANNING_TIME' | translate }} + + {{ completeTimestamp | harborDatetime : 'short' }} +
+
+
+ +
+ +
+ +
+ {{ 'SBOM.CHART.SCANNING_PERCENT' | translate }} + + {{ completePercent }} +
+
+ {{ + 'SBOM.CHART.SCANNING_PERCENT_EXPLAIN' | translate + }} +
+
+
+
diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.scss b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.scss new file mode 100644 index 000000000..973fa7e1a --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.scss @@ -0,0 +1,60 @@ +.tip-wrapper { + display: flex; + align-items: center; + color: #fff; + text-align: center; + font-size: 10px; + height: 15px; + line-height: 15px; +} + +.bar-scanning-time { + margin-top: 12px; +} + +.bar-tooltip-font-larger { + span { + font-size: 16px; + vertical-align: middle + } +} + +hr { + border-bottom: 0; + border-color: #aaa; + margin: 6px -10px; +} + +.font-weight-600 { + font-weight: 600; +} + +.margin-left-5 { + margin-left: 5px; +} + +.width-215 { + width: 215px; +} + +.width-150 { + width: 150px; +} + +.level-border>div{ + display: inline-flex; + align-items: center; + justify-items: center; + border-radius: 50%; + height: 26px; + width: 26px; + line-height: 26px; +} + +.tip-block { + position: relative; +} + +.margin-right-5 { + margin-right: 5px; +} diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.spec.ts new file mode 100644 index 000000000..f130dad4f --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.spec.ts @@ -0,0 +1,103 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ClarityModule } from '@clr/angular'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ActivatedRoute, Router } from '@angular/router'; +import { SbomTipHistogramComponent } from './sbom-tip-histogram.component'; +import { of } from 'rxjs'; +import { Project } from '../../../../../../../app/base/project/project'; +import { Artifact } from 'ng-swagger-gen/models'; + +describe('SbomTipHistogramComponent', () => { + let component: SbomTipHistogramComponent; + let fixture: ComponentFixture; + const mockRouter = { + navigate: () => {}, + }; + const mockedArtifact: Artifact = { + id: 123, + type: 'IMAGE', + }; + const mockedScanner = { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }; + const mockActivatedRoute = { + RouterparamMap: of({ get: key => 'value' }), + snapshot: { + params: { + repo: 'test', + digest: 'ABC', + subscribe: () => { + return of(null); + }, + }, + parent: { + params: { + id: 1, + }, + }, + data: { + artifactResolver: [mockedArtifact, new Project()], + }, + }, + data: of({ + projectResolver: { + ismember: true, + role_name: 'maintainer', + }, + }), + }; + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + ClarityModule, + TranslateModule.forRoot(), + ], + providers: [ + TranslateService, + { provide: Router, useValue: mockRouter }, + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + ], + declarations: [SbomTipHistogramComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(SbomTipHistogramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('Test SbomTipHistogramComponent basic functions', () => { + fixture.whenStable().then(() => { + expect(component).toBeTruthy(); + expect(component.isLimitedSuccess()).toBeFalsy(); + expect(component.noSbom).toBeTruthy(); + expect(component.isThemeLight()).toBeFalsy(); + expect(component.duration()).toBe('0'); + expect(component.completePercent).toBe('0%'); + }); + }); + + it('Test SbomTipHistogramComponent completeTimestamp', () => { + fixture.whenStable().then(() => { + component.sbomSummary.end_time = new Date('2024-04-08 00:01:02'); + expect(component.completeTimestamp).toBe( + component.sbomSummary.end_time + ); + }); + }); + + it('Test SbomTipHistogramComponent getScannerInfo', () => { + fixture.whenStable().then(() => { + expect(component.getScannerInfo()).toBe(''); + component.scanner = mockedScanner; + expect(component.getScannerInfo()).toBe( + `${mockedScanner.name}@${mockedScanner.version}` + ); + }); + }); +}); diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts new file mode 100644 index 000000000..2e948442b --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts @@ -0,0 +1,111 @@ +import { Component, Input } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ScannerVo, SbomSummary } from '../../../../../../shared/services'; +import { SBOM_SCAN_STATUS } from '../../../../../../shared/units/utils'; +import { + UN_LOGGED_PARAM, + YES, +} from '../../../../../../account/sign-in/sign-in.service'; +import { HAS_STYLE_MODE, StyleMode } from '../../../../../../services/theme'; +import { ScanTypes } from '../../../../../../shared/entities/shared.const'; + +const MIN = 60; +const MIN_STR = 'min '; +const SEC_STR = 'sec'; +const SUCCESS_PCT: number = 100; + +@Component({ + selector: 'hbr-sbom-tip-histogram', + templateUrl: './sbom-tip-histogram.component.html', + styleUrls: ['./sbom-tip-histogram.component.scss'], +}) +export class SbomTipHistogramComponent { + @Input() scanner: ScannerVo; + @Input() sbomSummary: SbomSummary = { + scan_status: SBOM_SCAN_STATUS.NOT_GENERATED_SBOM, + }; + @Input() artifactDigest: string = ''; + @Input() sbomDigest: string = ''; + constructor( + private translate: TranslateService, + private activatedRoute: ActivatedRoute, + private router: Router + ) {} + + duration(): string { + if (this.sbomSummary && this.sbomSummary.duration) { + let str = ''; + const min = Math.floor(this.sbomSummary.duration / MIN); + if (min) { + str += min + ' ' + MIN_STR; + } + const sec = this.sbomSummary.duration % MIN; + if (sec) { + str += sec + ' ' + SEC_STR; + } + return str; + } + return '0'; + } + + public get completePercent(): string { + return this.sbomSummary.scan_status === SBOM_SCAN_STATUS.SUCCESS + ? `100%` + : '0%'; + } + isLimitedSuccess(): boolean { + return ( + this.sbomSummary && this.sbomSummary.complete_percent < SUCCESS_PCT + ); + } + get completeTimestamp(): Date { + return this.sbomSummary && this.sbomSummary.end_time + ? this.sbomSummary.end_time + : new Date(); + } + + get noSbom(): boolean { + return ( + this.sbomSummary.scan_status === SBOM_SCAN_STATUS.NOT_GENERATED_SBOM + ); + } + + isThemeLight() { + return localStorage.getItem(HAS_STYLE_MODE) === StyleMode.LIGHT; + } + + getScannerInfo(): string { + if (this.scanner) { + if (this.scanner.name && this.scanner.version) { + return `${this.scanner.name}@${this.scanner.version}`; + } + if (this.scanner.name && !this.scanner.version) { + return `${this.scanner.name}`; + } + } + return ''; + } + + goIntoArtifactSbomSummaryPage(): void { + const relativeRouterLink: string[] = ['artifacts', this.artifactDigest]; + if (this.activatedRoute.snapshot.queryParams[UN_LOGGED_PARAM] === YES) { + this.router.navigate(relativeRouterLink, { + relativeTo: this.activatedRoute, + queryParams: { + [UN_LOGGED_PARAM]: YES, + sbomDigest: this.sbomDigest ?? '', + tab: ScanTypes.SBOM, + }, + }); + } else { + this.router.navigate(relativeRouterLink, { + relativeTo: this.activatedRoute, + queryParams: { + sbomDigest: this.sbomDigest ?? '', + tab: ScanTypes.SBOM, + }, + }); + } + } +} diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/scanning.scss b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/scanning.scss new file mode 100644 index 000000000..248361e33 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/scanning.scss @@ -0,0 +1,172 @@ +.bar-wrapper { + width: 210px; +} + +.bar-state { + .unknow-text { + margin-left: -5px; + } + + .label { + width: 50%; + } +} + +.bar-state-chart { + .loop-height { + height: 2px; + } +} + +.bar-state-error { + position: relative; +} + +.error-text { + position: relative; + top: 1px; + margin-left: -5px; + cursor: pointer; +} + +.scanning-button { + height: 24px; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + top: -12px; + position: relative; +} + +.tip-wrapper { + display: inline-block; + height: 10px; + max-width: 120px; +} + + +.bar-tooltip-font-title { + font-weight: 600; +} + +.bar-summary { + margin-top: 12px; + text-align: left; +} + +.bar-scanning-time { + margin-top: 12px; +} + +.bar-summary-item { + margin-top: 3px; + margin-bottom: 3px; +} + +.bar-summary-item span:nth-child(1){ + width: 30px; + text-align: center; + display: inline-block; +} + +.bar-summary-item span:nth-child(2){ + width: 28px; + display: inline-block; +} + +.option-right { + padding-right: 16px; +} + +.refresh-btn { + cursor: pointer; +} + +.refresh-btn:hover { + color: #007CBB; +} + +.tip-icon-medium { + color: orange; +} + +.tip-icon-low { + color: yellow; +} + +.font-color-green{ + color:green; +} +/* stylelint-disable */ +.bar-tooltip-font-larger span{ + font-size:16px; + vertical-align:middle +} + +hr{ + border-bottom: 0; + border-color: #aaa; + margin: 6px -10px; +} + +.font-weight-600{ + font-weight:600; +} + +.rightPos{ + position: absolute; + z-index: 100; + right: 35px; + margin-top: 4px; +} + +.result-row { + position: relative; +} + +.help-icon { + margin-left: 3px; +} + +.mt-3px { + margin-top: 5px; +} + +.label-critical { + background:#ff4d2e; + color:#000; +} + +.label-danger { + background:#ff8f3d!important; + color:#000!important; +} + +.label-medium { + background-color: #ffce66; + color:#000; +} + +.label-low { + background: #fff1ad; + color:#000; +} + +.label-none { + background-color: #2ec0ff; + color:#000; +} + +.no-border { + border: none; +} + +hbr-vulnerability-bar { + .label,.not-scan { + width: 90%; + } +} + +.stopped { + border-color: #cccc15; +} diff --git a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts index 132549186..9f840d3ec 100644 --- a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts @@ -11,14 +11,43 @@ import { import { VULNERABILITY_SCAN_STATUS } from '../../../../../shared/units/utils'; import { NativeReportSummary } from '../../../../../../../ng-swagger-gen/models/native-report-summary'; import { SharedTestingModule } from '../../../../../shared/shared.module'; +import { of, timer } from 'rxjs'; +import { ArtifactService, ScanService } from 'ng-swagger-gen/services'; describe('ResultBarChartComponent (inline template)', () => { let component: ResultBarChartComponent; let fixture: ComponentFixture; + const mockedSbomDigest = + 'sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3'; let mockData: NativeReportSummary = { + report_id: '12345', scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS, severity: 'High', end_time: new Date().toUTCString(), + scanner: { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }, + summary: { + total: 124, + fixable: 50, + summary: { + High: 5, + Low: 5, + }, + }, + }; + let mockCloneData: NativeReportSummary = { + report_id: '123456', + scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS, + severity: 'High', + end_time: new Date().toUTCString(), + scanner: { + name: 'Trivy', + vendor: 'vm', + version: 'v1.3', + }, summary: { total: 124, fixable: 50, @@ -29,6 +58,59 @@ describe('ResultBarChartComponent (inline template)', () => { }, }; + const FakedScanService = { + scanArtifact: () => of({}), + stopScanArtifact: () => of({}), + }; + const FakedArtifactService = { + getArtifact: () => + of({ + accessories: null, + addition_links: { + build_history: { + absolute: false, + href: '/api/v2.0/projects/xuel/repositories/ui%252Fserver%252Fconfig-dev/artifacts/sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3/additions/build_history', + }, + vulnerabilities: { + absolute: false, + href: '/api/v2.0/projects/xuel/repositories/ui%252Fserver%252Fconfig-dev/artifacts/sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3/additions/vulnerabilities', + }, + }, + digest: 'sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3', + extra_attrs: { + architecture: 'amd64', + author: '', + config: { + Env: [ + 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', + ], + WorkingDir: '/', + }, + created: '2024-01-10T10:05:33.2702206Z', + os: 'linux', + }, + icon: 'sha256:0048162a053eef4d4ce3fe7518615bef084403614f8bca43b40ae2e762e11e06', + id: 3, + labels: null, + manifest_media_type: + 'application/vnd.docker.distribution.manifest.v2+json', + media_type: 'application/vnd.docker.container.image.v1+json', + project_id: 3, + pull_time: '2024-04-02T01:50:58.332Z', + push_time: '2024-03-06T09:47:08.163Z', + references: null, + repository_id: 2, + scan_overview: { + duration: 2, + end_time: '2024-04-02T01:50:59.406Z', + scan_status: 'Success', + start_time: '2024-04-02T01:50:57.176Z', + }, + size: 3957, + tags: null, + type: 'IMAGE', + }), + }; beforeEach(async () => { await TestBed.configureTestingModule({ imports: [SharedTestingModule], @@ -43,6 +125,14 @@ describe('ResultBarChartComponent (inline template)', () => { useValue: ScanningResultDefaultService, }, { provide: JobLogService, useValue: JobLogDefaultService }, + { + provide: ScanService, + useValue: FakedScanService, + }, + { + provide: ArtifactService, + useValue: FakedArtifactService, + }, ], }).compileComponents(); }); @@ -52,6 +142,8 @@ describe('ResultBarChartComponent (inline template)', () => { component = fixture.componentInstance; component.artifactDigest = 'mockTag'; component.summary = mockData; + component.repoName = 'mockRepo'; + component.artifactDigest = mockedSbomDigest; fixture.detectChanges(); }); @@ -109,4 +201,69 @@ describe('ResultBarChartComponent (inline template)', () => { expect(el).not.toBeNull(); }); }); + it('Test ResultBarChartComponent getScanner', () => { + fixture.detectChanges(); + component.summary = mockData; + expect(component.getScanner()).toBe(mockData.scanner); + component.projectName = 'test'; + component.repoName = 'ui'; + component.artifactDigest = 'dg'; + expect(component.viewLog()).toBe( + '/api/v2.0/projects/test/repositories/ui/artifacts/dg/scan/12345/log' + ); + component.copyValue(mockCloneData); + expect(component.summary.report_id).toBe(mockCloneData.report_id); + }); + it('Test ResultBarChartComponent status', () => { + fixture.detectChanges(); + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.SUCCESS; + expect(component.status).toBe(VULNERABILITY_SCAN_STATUS.SUCCESS); + expect(component.completed).toBeTruthy(); + expect(component.queued).toBeFalsy(); + expect(component.scanning).toBeFalsy(); + expect(component.stopped).toBeFalsy(); + expect(component.otherStatus).toBeFalsy(); + expect(component.error).toBeFalsy(); + }); + it('Test ResultBarChartComponent ngOnDestroy', () => { + component.stateCheckTimer = timer(0, 10000).subscribe(() => {}); + component.ngOnDestroy(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.stateCheckTimer).toBeNull(); + expect(component.scanSubscription).toBeNull(); + expect(component.stopSubscription).toBeNull(); + }); + }); + it('Test ResultBarChartComponent scanNow', () => { + fixture.detectChanges(); + component.scanNow(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onSubmitting).toBeFalse(); + }); + }); + it('Test ResultBarChartComponent stopScan', () => { + fixture.detectChanges(); + component.stopScan(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.onStopping).toBeFalse(); + expect(component.stateCheckTimer).toBeNull(); + }); + }); + it('Test ResultBarChartComponent getSummary', () => { + fixture.detectChanges(); + // component.summary.scan_status = VULNERABILITY_SCAN_STATUS.SUCCESS; + component.getSummary(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + expect(component.summary.scan_status).toBe( + VULNERABILITY_SCAN_STATUS.SUCCESS + ); + }); + }); }); diff --git a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts index 69568d550..ceb8e25b2 100644 --- a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts @@ -25,6 +25,8 @@ import { HarborEvent, } from '../../../../../services/event-service/event.service'; import { ScanService } from '../../../../../../../ng-swagger-gen/services/scan.service'; +import { ScanType } from 'ng-swagger-gen/models'; +import { ScanTypes } from '../../../../../shared/entities/shared.const'; const STATE_CHECK_INTERVAL: number = 3000; // 3s const RETRY_TIMES: number = 3; @@ -110,7 +112,7 @@ export class ResultBarChartComponent implements OnInit, OnDestroy { this.scanSubscription.unsubscribe(); this.scanSubscription = null; } - if (!this.stopSubscription) { + if (this.stopSubscription) { this.stopSubscription.unsubscribe(); this.stopSubscription = null; } @@ -171,6 +173,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy { projectName: this.projectName, reference: this.artifactDigest, repositoryName: dbEncodeURIComponent(this.repoName), + // scanType: { + // scan_type: ScanTypes.VULNERABILITY, + // }, }) .pipe(finalize(() => this.submitFinish.emit(false))) .subscribe( @@ -286,6 +291,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy { projectName: this.projectName, reference: this.artifactDigest, repositoryName: dbEncodeURIComponent(this.repoName), + scanType: { + scan_type: ScanTypes.VULNERABILITY, + }, }) .pipe( finalize(() => { diff --git a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/scanning.scss b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/scanning.scss index 5d1f98335..248361e33 100644 --- a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/scanning.scss +++ b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/scanning.scss @@ -8,7 +8,7 @@ } .label { - width: 90%; + width: 50%; } } diff --git a/src/portal/src/app/services/event-service/event.service.ts b/src/portal/src/app/services/event-service/event.service.ts index 88d5e28bd..31032f52d 100644 --- a/src/portal/src/app/services/event-service/event.service.ts +++ b/src/portal/src/app/services/event-service/event.service.ts @@ -76,8 +76,11 @@ export enum HarborEvent { SCROLL_TO_POSITION = 'scrollToPosition', REFRESH_PROJECT_INFO = 'refreshProjectInfo', START_SCAN_ARTIFACT = 'startScanArtifact', + START_GENERATE_SBOM = 'startGenerateSbom', STOP_SCAN_ARTIFACT = 'stopScanArtifact', + STOP_SBOM_ARTIFACT = 'stopSbomArtifact', UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo', + UPDATE_SBOM_INFO = 'UpdateSbomInfo', REFRESH_EXPORT_JOBS = 'refreshExportJobs', DELETE_ACCESSORY = 'deleteAccessory', COPY_DIGEST = 'copyDigest', diff --git a/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts b/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts index 77701f2a4..5aa9f8939 100644 --- a/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts +++ b/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts @@ -18,7 +18,7 @@ import { ActivatedRouteSnapshot, } from '@angular/router'; import { forkJoin, Observable, of } from 'rxjs'; -import { map, catchError, mergeMap } from 'rxjs/operators'; +import { catchError, mergeMap } from 'rxjs/operators'; import { Artifact } from '../../../../ng-swagger-gen/models/artifact'; import { ArtifactService } from '../../../../ng-swagger-gen/services/artifact.service'; import { Project } from '../../base/project/project'; @@ -51,6 +51,7 @@ export class ArtifactDetailRoutingResolverService { projectName: project.name, withLabel: true, withScanOverview: true, + // withSbomOverview: true, withTag: false, withImmutableStatus: true, }), diff --git a/src/portal/src/app/shared/entities/shared.const.ts b/src/portal/src/app/shared/entities/shared.const.ts index be9bdab5f..573cda40b 100644 --- a/src/portal/src/app/shared/entities/shared.const.ts +++ b/src/portal/src/app/shared/entities/shared.const.ts @@ -382,3 +382,8 @@ export const stringsForClarity: Partial = { datepickerSelectYearText: 'CLARITY.DATE_PICKER_SELECT_YEAR_TEXT', datepickerSelectedLabel: 'CLARITY.DATE_PICKER_SELECTED_LABEL', }; + +export enum ScanTypes { + SBOM = 'sbom', + VULNERABILITY = 'vulnerability', +} diff --git a/src/portal/src/app/shared/services/interface.ts b/src/portal/src/app/shared/services/interface.ts index 5d641c99a..1afbc36af 100644 --- a/src/portal/src/app/shared/services/interface.ts +++ b/src/portal/src/app/shared/services/interface.ts @@ -211,6 +211,16 @@ export interface VulnerabilitySummary { scanner?: ScannerVo; complete_percent?: number; } +export interface SbomSummary { + report_id?: string; + sbom_digest?: string; + scan_status?: string; + duration?: number; + start_time?: Date; + end_time?: Date; + scanner?: ScannerVo; + complete_percent?: number; +} export interface ScannerVo { name?: string; vendor?: string; diff --git a/src/portal/src/app/shared/services/permission-static.ts b/src/portal/src/app/shared/services/permission-static.ts index 125b15f94..fc15a0303 100644 --- a/src/portal/src/app/shared/services/permission-static.ts +++ b/src/portal/src/app/shared/services/permission-static.ts @@ -105,6 +105,13 @@ export const USERSTATICPERMISSION = { READ: 'read', }, }, + REPOSITORY_TAG_SBOM_JOB: { + KEY: 'sbom', + VALUE: { + CREATE: 'create', + READ: 'read', + }, + }, REPOSITORY_ARTIFACT_LABEL: { KEY: 'artifact-label', VALUE: { diff --git a/src/portal/src/app/shared/units/utils.ts b/src/portal/src/app/shared/units/utils.ts index ad38954ac..4efa2eadb 100644 --- a/src/portal/src/app/shared/units/utils.ts +++ b/src/portal/src/app/shared/units/utils.ts @@ -275,6 +275,21 @@ export const VULNERABILITY_SCAN_STATUS = { SUCCESS: 'Success', SCHEDULED: 'Scheduled', }; + +/** + * The state of sbom generation + */ +export const SBOM_SCAN_STATUS = { + // front-end status + NOT_GENERATED_SBOM: 'Not generated SBOM', + // back-end status + PENDING: 'Pending', + RUNNING: 'Running', + ERROR: 'Error', + STOPPED: 'Stopped', + SUCCESS: 'Success', + SCHEDULED: 'Scheduled', +}; /** * The severity of vulnerability scanning */ diff --git a/src/portal/src/i18n/lang/de-de-lang.json b/src/portal/src/i18n/lang/de-de-lang.json index acf0e8fdb..cae7b7af0 100644 --- a/src/portal/src/i18n/lang/de-de-lang.json +++ b/src/portal/src/i18n/lang/de-de-lang.json @@ -777,6 +777,7 @@ "ARTIFACTS": "Artefakte", "SIZE": "Größe", "VULNERABILITY": "Schwachstellen", + "SBOM": "SBOM", "BUILD_HISTORY": "Build History", "SIGNED": "Signiert", "AUTHOR": "Autor", @@ -1027,6 +1028,41 @@ "IN_PROGRESS": "Suche...", "BACK": "Zurück" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Nicht gescannt", @@ -1107,6 +1143,7 @@ "ALL": "Alle", "PLACEHOLDER": "Keine Artefakte gefunden!", "SCAN_UNSUPPORTED": "Nicht unterstützt", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Zusammenfassung", "DEPENDENCIES": "Dependencies", "VALUES": "Values", diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 243ceee98..93b802401 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -778,6 +778,7 @@ "ARTIFACTS": "Artifacts", "SIZE": "Size", "VULNERABILITY": "Vulnerabilities", + "SBOM": "SBOM", "BUILD_HISTORY": "Build History", "SIGNED": "Signed", "AUTHOR": "Author", @@ -1028,6 +1029,41 @@ "IN_PROGRESS": "Search...", "BACK": "Back" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Not Scanned", @@ -1108,6 +1144,7 @@ "ALL": "All", "PLACEHOLDER": "We couldn't find any artifacts!", "SCAN_UNSUPPORTED": "Unsupported", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Summary", "DEPENDENCIES": "Dependencies", "VALUES": "Values", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index cce410e54..11dc5b0b5 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -778,6 +778,7 @@ "ARTIFACTS": "Artifacts", "SIZE": "Size", "VULNERABILITY": "Vulnerabilities", + "SBOM": "SBOM", "BUILD_HISTORY": "Construir Historia", "SIGNED": "Firmada", "AUTHOR": "Autor", @@ -1026,6 +1027,41 @@ "IN_PROGRESS": "Buscar...", "BACK": "Volver" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Not Scanned", @@ -1106,6 +1142,7 @@ "ALL": "All", "PLACEHOLDER": "We couldn't find any artifacts!", "SCAN_UNSUPPORTED": "Unsupported", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Summary", "DEPENDENCIES": "Dependencies", "VALUES": "Values", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index 035bb3a05..f09c6ba66 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -777,6 +777,7 @@ "ARTIFACTS": "Artefacts", "SIZE": "Taille", "VULNERABILITY": "Vulnérabilité", + "SBOM": "SBOM", "BUILD_HISTORY": "Historique de construction", "SIGNED": "Signé", "AUTHOR": "Auteur", @@ -1026,6 +1027,41 @@ "IN_PROGRESS": "Recherche...", "BACK": "Retour" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Non analysé", @@ -1106,6 +1142,7 @@ "ALL": "Tous", "PLACEHOLDER": "Nous n'avons trouvé aucun artefact !", "SCAN_UNSUPPORTED": "Non supporté", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Résumé", "DEPENDENCIES": "Dépendances", "VALUES": "Valeurs", diff --git a/src/portal/src/i18n/lang/ko-kr-lang.json b/src/portal/src/i18n/lang/ko-kr-lang.json index 20de6aa90..49f16e841 100644 --- a/src/portal/src/i18n/lang/ko-kr-lang.json +++ b/src/portal/src/i18n/lang/ko-kr-lang.json @@ -775,6 +775,7 @@ "ARTIFACTS": "아티팩트들", "SIZE": "크기", "VULNERABILITY": "취약점", + "SBOM": "SBOM", "BUILD_HISTORY": "기록 생성", "SIGNED": "서명됨", "AUTHOR": "작성자", @@ -1025,6 +1026,41 @@ "IN_PROGRESS": "검색 중...", "BACK": "뒤로" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "스캔되지 않음", @@ -1105,6 +1141,7 @@ "ALL": "모두", "PLACEHOLDER": "아티팩트를 찾을 수 없음!", "SCAN_UNSUPPORTED": "지원되지 않음", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "요약", "DEPENDENCIES": "종속성", "VALUES": "값", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 31243b5c3..2ba78f122 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -776,6 +776,7 @@ "ARTIFACTS": "Artefatos", "SIZE": "Tamanho", "VULNERABILITY": "Vulnerabilidade", + "SBOM": "SBOM", "SIGNED": "Assinada", "AUTHOR": "Autor", "CREATED": "Data de criação", @@ -1024,6 +1025,41 @@ "IN_PROGRESS": "Buscando...", "BACK": "Voltar" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Não analisado", @@ -1104,6 +1140,7 @@ "ALL": "Todos", "PLACEHOLDER": "Nenhum artefato encontrado!", "SCAN_UNSUPPORTED": "Não pode ser examinada", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Resumo", "DEPENDENCIES": "Dependências", "VALUES": "Valores", diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index 82be0f11f..0dddab935 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -777,6 +777,7 @@ "ARTIFACTS": "Artifacts", "SIZE": "Boyut", "VULNERABILITY": "Güvenlik Açığı", + "SBOM": "SBOM", "BUILD_HISTORY": "Geçmişi Oluştur", "SIGNED": "İmzalanmış", "AUTHOR": "Yazar", @@ -1027,6 +1028,41 @@ "IN_PROGRESS": "Ara...", "BACK": "Geri" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "Taranmadı", @@ -1107,6 +1143,7 @@ "ALL": "All", "PLACEHOLDER": "We couldn't find any artifacts!", "SCAN_UNSUPPORTED": "Unsupported", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "Özet", "DEPENDENCIES": "Bağımlılıklar", "VALUES": "Değerler", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index b738a3fdf..bb2a58578 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -776,6 +776,7 @@ "ARTIFACTS": "Artifacts", "SIZE": "大小", "VULNERABILITY": "漏洞", + "SBOM": "SBOM", "BUILD_HISTORY": "构建历史", "SIGNED": "已签名", "AUTHOR": "作者", @@ -1025,6 +1026,41 @@ "IN_PROGRESS": "搜索中...", "BACK": "返回" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" + }, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "未扫描", @@ -1105,6 +1141,7 @@ "ALL": "所有", "PLACEHOLDER": "未发现任何 artifacts!", "SCAN_UNSUPPORTED": "不支持扫描", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "概要", "DEPENDENCIES": "依赖", "VALUES": "取值", diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json index 4553e3445..bc1732775 100644 --- a/src/portal/src/i18n/lang/zh-tw-lang.json +++ b/src/portal/src/i18n/lang/zh-tw-lang.json @@ -776,6 +776,7 @@ "ARTIFACTS": "Artifacts", "SIZE": "大小", "VULNERABILITY": "弱點", + "SBOM": "SBOM", "BUILD_HISTORY": "建置歷史", "SIGNED": "已簽署", "AUTHOR": "作者", @@ -1024,6 +1025,41 @@ "IN_PROGRESS": "搜尋中...", "BACK": "返回" }, + "SBOM": { + "CHART": { + "SCANNING_TIME": "Scan completed time:", + "SCANNING_PERCENT": "Scan progress:", + "SCANNING_PERCENT_EXPLAIN": "The scan completion progress is calculated as # of successfully scanned images / total number of images referenced within the image index.", + "TOOLTIPS_TITLE": "{{totalSbom}} of {{totalPackages}} {{package}} have known {{sbom}}.", + "TOOLTIPS_TITLE_SINGULAR": "{{totalSbom}} of {{totalPackages}} {{package}} has known {{sbom}}.", + "TOOLTIPS_TITLE_ZERO": "No recognizable SBOM detected" + }, + "GRID": { + "PLACEHOLDER": "No scan results found.", + "COLUMN_PACKAGE": "Package", + "COLUMN_PACKAGES": "Packages", + "COLUMN_VERSION": "Current version", + "COLUMN_LICENSE": "License", + "COLUMN_DESCRIPTION": "Description", + "FOOT_ITEMS": "Items", + "FOOT_OF": "of" + }, + "STATE": { + "OTHER_STATUS": "Not Generated", + "QUEUED": "Queued", + "ERROR": "View Log", + "SCANNING": "Generating", + "STOPPED": "SBOM scan stopped" + }, + "NO_SBOM": "No SBOM", + "PACKAGES": "SBOM", + "REPORTED_BY": "Reported by {{scanner}}", + "GENERATE": "Create SBOM", + "DOWNLOAD": "Download SBOM", + "Details": "SBOM details", + "STOP": "Stop SBOM", + "TRIGGER_STOP_SUCCESS": "Trigger stopping SBOM generation successfully" +}, "VULNERABILITY": { "STATE": { "OTHER_STATUS": "未掃描", @@ -1104,6 +1140,7 @@ "ALL": "全部", "PLACEHOLDER": "未發現任何 artifacts!", "SCAN_UNSUPPORTED": "不支援掃描", + "SBOM_UNSUPPORTED": "Unsupported", "SUMMARY": "摘要", "DEPENDENCIES": "相依性", "VALUES": "值", diff --git a/src/server/v2.0/handler/scan.go b/src/server/v2.0/handler/scan.go index e58e12a84..cca0092e8 100644 --- a/src/server/v2.0/handler/scan.go +++ b/src/server/v2.0/handler/scan.go @@ -60,7 +60,7 @@ func (s *scanAPI) StopScanArtifact(ctx context.Context, params operation.StopSca return s.SendError(ctx, err) } - if err := s.scanCtl.Stop(ctx, curArtifact); err != nil { + if err := s.scanCtl.Stop(ctx, curArtifact, params.ScanType.ScanType); err != nil { return s.SendError(ctx, err) } diff --git a/src/server/v2.0/handler/scan_test.go b/src/server/v2.0/handler/scan_test.go index f073b83e9..e0977b80c 100644 --- a/src/server/v2.0/handler/scan_test.go +++ b/src/server/v2.0/handler/scan_test.go @@ -16,6 +16,7 @@ package handler import ( "fmt" + "github.com/goharbor/harbor/src/server/v2.0/models" "testing" "github.com/stretchr/testify/suite" @@ -67,12 +68,12 @@ func (suite *ScanTestSuite) TestStopScan() { suite.Security.On("Can", mock.Anything, mock.Anything, mock.Anything).Return(true).Times(times) url := "/projects/library/repositories/nginx/artifacts/sha256:e4f0474a75c510f40b37b6b7dc2516241ffa8bde5a442bde3d372c9519c84d90/scan/stop" - + body := models.ScanType{ScanType: "sbom"} { // failed to get artifact by reference mock.OnAnything(suite.artifactCtl, "GetByReference").Return(&artifact.Artifact{}, fmt.Errorf("failed to get artifact by reference")).Once() - res, err := suite.Post(url, nil) + res, err := suite.PostJSON(url, body) suite.NoError(err) suite.Equal(500, res.StatusCode) } @@ -82,7 +83,7 @@ func (suite *ScanTestSuite) TestStopScan() { mock.OnAnything(suite.artifactCtl, "GetByReference").Return(nil, nil).Once() mock.OnAnything(suite.scanCtl, "Stop").Return(fmt.Errorf("nil artifact to stop scan")).Once() - res, err := suite.Post(url, nil) + res, err := suite.PostJSON(url, body) suite.NoError(err) suite.Equal(500, res.StatusCode) } @@ -92,7 +93,7 @@ func (suite *ScanTestSuite) TestStopScan() { mock.OnAnything(suite.artifactCtl, "GetByReference").Return(&artifact.Artifact{}, nil).Once() mock.OnAnything(suite.scanCtl, "Stop").Return(nil).Once() - res, err := suite.Post(url, nil) + res, err := suite.PostJSON(url, body) suite.NoError(err) suite.Equal(202, res.StatusCode) } diff --git a/src/testing/controller/scan/controller.go b/src/testing/controller/scan/controller.go index 2efdd8091..bd8001380 100644 --- a/src/testing/controller/scan/controller.go +++ b/src/testing/controller/scan/controller.go @@ -191,13 +191,13 @@ func (_m *Controller) ScanAll(ctx context.Context, trigger string, async bool) ( return r0, r1 } -// Stop provides a mock function with given fields: ctx, _a1 -func (_m *Controller) Stop(ctx context.Context, _a1 *artifact.Artifact) error { - ret := _m.Called(ctx, _a1) +// Stop provides a mock function with given fields: ctx, _a1, capType +func (_m *Controller) Stop(ctx context.Context, _a1 *artifact.Artifact, capType string) error { + ret := _m.Called(ctx, _a1, capType) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact) error); ok { - r0 = rf(ctx, _a1) + if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, string) error); ok { + r0 = rf(ctx, _a1, capType) } else { r0 = ret.Error(0) } diff --git a/tests/apitests/python/library/scan_stop.py b/tests/apitests/python/library/scan_stop.py index 9f4bfd85c..e002239b2 100644 --- a/tests/apitests/python/library/scan_stop.py +++ b/tests/apitests/python/library/scan_stop.py @@ -11,7 +11,9 @@ class StopScan(base.Base, object): def stop_scan_artifact(self, project_name, repo_name, reference, expect_status_code = 202, expect_response_body = None, **kwargs): try: - data, status_code, _ = self._get_client(**kwargs).stop_scan_artifact_with_http_info(project_name, repo_name, reference) + scanType = v2_swagger_client.ScanType() + scanType.scan_type = "vulnerability" + data, status_code, _ = self._get_client(**kwargs).stop_scan_artifact_with_http_info(project_name, repo_name, reference, scanType) except ApiException as e: base._assert_status_code(expect_status_code, e.status) if expect_response_body is not None: diff --git a/tests/ci/api_run.sh b/tests/ci/api_run.sh index 83f60ffd8..26fdd5f81 100755 --- a/tests/ci/api_run.sh +++ b/tests/ci/api_run.sh @@ -21,7 +21,7 @@ set +e docker ps # run db auth api cases if [ "$1" = 'DB' ]; then - docker run -i --privileged -v $DIR/../../:/drone -v $DIR/../:/ca -w /drone $E2E_IMAGE robot --exclude proxy_cache -v DOCKER_USER:${DOCKER_USER} -v DOCKER_PWD:${DOCKER_PWD} -v ip:$2 -v ip1: -v http_get_ca:false -v HARBOR_PASSWORD:Harbor12345 /drone/tests/robot-cases/Group1-Nightly/Setup.robot /drone/tests/robot-cases/Group0-BAT/API_DB.robot + docker run -i --privileged -v $DIR/../../:/drone -v $DIR/../:/ca -w /drone $E2E_IMAGE robot --exclude proxy_cache --exclude stop_scan -v DOCKER_USER:${DOCKER_USER} -v DOCKER_PWD:${DOCKER_PWD} -v ip:$2 -v ip1: -v http_get_ca:false -v HARBOR_PASSWORD:Harbor12345 /drone/tests/robot-cases/Group1-Nightly/Setup.robot /drone/tests/robot-cases/Group0-BAT/API_DB.robot elif [ "$1" = 'PROXY_CACHE' ]; then docker run -i --privileged -v $DIR/../../:/drone -v $DIR/../:/ca -w /drone $E2E_IMAGE robot --include setup --include proxy_cache -v DOCKER_USER:${DOCKER_USER} -v DOCKER_PWD:${DOCKER_PWD} -v ip:$2 -v ip1: -v http_get_ca:false -v HARBOR_PASSWORD:Harbor12345 /drone/tests/robot-cases/Group1-Nightly/Setup.robot /drone/tests/robot-cases/Group0-BAT/API_DB.robot elif [ "$1" = 'LDAP' ]; then From 03d9575d845594e81ed29c5a868a3acf844df0b2 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Tue, 9 Apr 2024 16:50:46 +0800 Subject: [PATCH 005/100] update referrer manifest descriptor size (#20207) cache manifest when first time pull if cacheEnabled Signed-off-by: yminer --- src/server/registry/referrers.go | 59 ++++++++++-- src/server/registry/referrers_test.go | 129 +++++++++++++++++++++++++- 2 files changed, 177 insertions(+), 11 deletions(-) diff --git a/src/server/registry/referrers.go b/src/server/registry/referrers.go index ee715faba..00504dfd4 100644 --- a/src/server/registry/referrers.go +++ b/src/server/registry/referrers.go @@ -22,11 +22,16 @@ import ( "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/goharbor/harbor/src/lib/cache" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" lib_http "github.com/goharbor/harbor/src/lib/http" + "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/q" "github.com/goharbor/harbor/src/pkg/accessory" "github.com/goharbor/harbor/src/pkg/artifact" + "github.com/goharbor/harbor/src/pkg/cached/manifest/redis" + "github.com/goharbor/harbor/src/pkg/registry" "github.com/goharbor/harbor/src/server/router" "github.com/goharbor/harbor/src/server/v2.0/handler" ) @@ -38,12 +43,16 @@ func newReferrersHandler() http.Handler { return &referrersHandler{ artifactManager: artifact.NewManager(), accessoryManager: accessory.NewManager(), + registryClient: registry.Cli, + maniCacheManager: redis.NewManager(), } } type referrersHandler struct { artifactManager artifact.Manager accessoryManager accessory.Manager + registryClient registry.Client + maniCacheManager redis.CachedManager } func (r *referrersHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { @@ -75,18 +84,56 @@ func (r *referrersHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { lib_http.SendError(w, err) return } - // Build index manifest from accessories mfs := make([]ocispec.Descriptor, 0) for _, acc := range accs { - accArt, err := r.artifactManager.GetByDigest(ctx, repository, acc.GetData().Digest) + accArtDigest := acc.GetData().Digest + accArt, err := r.artifactManager.GetByDigest(ctx, repository, accArtDigest) if err != nil { lib_http.SendError(w, err) return } - mf := ocispec.Descriptor{ + // whether get manifest from cache + fromCache := false + // whether need write manifest to cache + writeCache := false + var maniContent []byte + + // pull manifest, will try to pull from cache first + // and write to cache when pull manifest from registry at first time + if config.CacheEnabled() { + maniContent, err = r.maniCacheManager.Get(req.Context(), accArtDigest) + if err == nil { + fromCache = true + } else { + log.Debugf("failed to get manifest %s from cache, will fallback to registry, error: %v", accArtDigest, err) + if errors.As(err, &cache.ErrNotFound) { + writeCache = true + } + } + } + if !fromCache { + mani, _, err := r.registryClient.PullManifest(accArt.RepositoryName, accArtDigest) + if err != nil { + lib_http.SendError(w, err) + return + } + _, maniContent, err = mani.Payload() + if err != nil { + lib_http.SendError(w, err) + return + } + // write manifest to cache when first time pulling + if writeCache { + err = r.maniCacheManager.Save(req.Context(), accArtDigest, maniContent) + if err != nil { + log.Warningf("failed to save accArt manifest %s to cache, error: %v", accArtDigest, err) + } + } + } + desc := ocispec.Descriptor{ MediaType: accArt.ManifestMediaType, - Size: accArt.Size, + Size: int64(len(maniContent)), Digest: digest.Digest(accArt.Digest), Annotations: accArt.Annotations, ArtifactType: accArt.ArtifactType, @@ -94,10 +141,10 @@ func (r *referrersHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // filter use accArt.ArtifactType as artifactType if at != "" { if accArt.ArtifactType == at { - mfs = append(mfs, mf) + mfs = append(mfs, desc) } } else { - mfs = append(mfs, mf) + mfs = append(mfs, desc) } } diff --git a/src/server/registry/referrers_test.go b/src/server/registry/referrers_test.go index f8d8abc22..5eab49ee7 100644 --- a/src/server/registry/referrers_test.go +++ b/src/server/registry/referrers_test.go @@ -3,20 +3,50 @@ package registry import ( "context" "encoding/json" + "fmt" "net/http" "net/http/httptest" "testing" beegocontext "github.com/beego/beego/v2/server/web/context" ocispec "github.com/opencontainers/image-spec/specs-go/v1" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/assert" + "github.com/goharbor/harbor/src/lib/cache" + "github.com/goharbor/harbor/src/lib/config" accessorymodel "github.com/goharbor/harbor/src/pkg/accessory/model" basemodel "github.com/goharbor/harbor/src/pkg/accessory/model/base" "github.com/goharbor/harbor/src/pkg/artifact" + "github.com/goharbor/harbor/src/pkg/distribution" "github.com/goharbor/harbor/src/server/router" "github.com/goharbor/harbor/src/testing/mock" accessorytesting "github.com/goharbor/harbor/src/testing/pkg/accessory" arttesting "github.com/goharbor/harbor/src/testing/pkg/artifact" + testmanifest "github.com/goharbor/harbor/src/testing/pkg/cached/manifest/redis" + regtesting "github.com/goharbor/harbor/src/testing/pkg/registry" +) + +var ( + OCIManifest = `{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.example.sbom", + "digest": "sha256:5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03", + "size": 123 + }, + "layers": [ + { + "mediaType": "application/vnd.example.data.v1.tar+gzip", + "digest": "sha256:e258d248fda94c63753607f7c4494ee0fcbe92f1a76bfdac795c9d84101eb317", + "size": 1234 + } + ], + "annotations": { + "name": "test-image" + } + }` ) func TestReferrersHandlerOK(t *testing.T) { @@ -35,10 +65,10 @@ func TestReferrersHandlerOK(t *testing.T) { artifactMock.On("GetByDigest", mock.Anything, mock.Anything, mock.Anything). Return(&artifact.Artifact{ - Digest: digestVal, + Digest: "sha256:4911bb745e19a6b5513755f3d033f10ef10c34b40edc631809e28be8a7c005f6", ManifestMediaType: "application/vnd.oci.image.manifest.v1+json", MediaType: "application/vnd.example.sbom", - ArtifactType: "application/vnd.example+type", + ArtifactType: "application/vnd.example.sbom", Size: 1000, Annotations: map[string]string{ "name": "test-image", @@ -56,13 +86,23 @@ func TestReferrersHandlerOK(t *testing.T) { SubArtifactDigest: digestVal, SubArtifactRepo: "goharbor", Type: accessorymodel.TypeCosignSignature, + Digest: "sha256:4911bb745e19a6b5513755f3d033f10ef10c34b40edc631809e28be8a7c005f6", }, }, }, nil) + manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(OCIManifest)) + if err != nil { + t.Fatal(err) + } + regCliMock := ®testing.Client{} + config.DefaultMgr().Set(context.TODO(), "cache_enabled", false) + mock.OnAnything(regCliMock, "PullManifest").Return(manifest, "", nil) + handler := &referrersHandler{ artifactManager: artifactMock, accessoryManager: accessoryMock, + registryClient: regCliMock, } handler.ServeHTTP(rec, req) @@ -72,10 +112,89 @@ func TestReferrersHandlerOK(t *testing.T) { t.Errorf("Expected status code %d, but got %d", http.StatusOK, rec.Code) } index := &ocispec.Index{} - json.Unmarshal([]byte(rec.Body.String()), index) - if index.Manifests[0].ArtifactType != "application/vnd.example+type" { - t.Errorf("Expected response body %s, but got %s", "application/vnd.example+type", rec.Body.String()) + json.Unmarshal(rec.Body.Bytes(), index) + if index.Manifests[0].ArtifactType != "application/vnd.example.sbom" { + t.Errorf("Expected response body %s, but got %s", "application/vnd.example.sbom", rec.Body.String()) } + _, content, _ := manifest.Payload() + assert.Equal(t, int64(len(content)), index.Manifests[0].Size) +} + +func TestReferrersHandlerSavetoCache(t *testing.T) { + rec := httptest.NewRecorder() + digestVal := "sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b" + req, err := http.NewRequest("GET", "/v2/test/repository/referrers/sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b", nil) + if err != nil { + t.Fatal(err) + } + input := &beegocontext.BeegoInput{} + input.SetParam(":reference", digestVal) + *req = *(req.WithContext(context.WithValue(req.Context(), router.ContextKeyInput{}, input))) + + artifactMock := &arttesting.Manager{} + accessoryMock := &accessorytesting.Manager{} + + artifactMock.On("GetByDigest", mock.Anything, mock.Anything, mock.Anything). + Return(&artifact.Artifact{ + Digest: "sha256:4911bb745e19a6b5513755f3d033f10ef10c34b40edc631809e28be8a7c005f6", + ManifestMediaType: "application/vnd.oci.image.manifest.v1+json", + MediaType: "application/vnd.example.sbom", + ArtifactType: "application/vnd.example.sbom", + Size: 1000, + Annotations: map[string]string{ + "name": "test-image", + }, + }, nil) + + accessoryMock.On("Count", mock.Anything, mock.Anything). + Return(int64(1), nil) + accessoryMock.On("List", mock.Anything, mock.Anything). + Return([]accessorymodel.Accessory{ + &basemodel.Default{ + Data: accessorymodel.AccessoryData{ + ID: 1, + ArtifactID: 2, + SubArtifactDigest: digestVal, + SubArtifactRepo: "goharbor", + Type: accessorymodel.TypeCosignSignature, + Digest: "sha256:4911bb745e19a6b5513755f3d033f10ef10c34b40edc631809e28be8a7c005f6", + }, + }, + }, nil) + + manifest, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(OCIManifest)) + if err != nil { + t.Fatal(err) + } + + // cache_enabled pull from cahce + config.DefaultMgr().Set(context.TODO(), "cache_enabled", true) + cacheManagerMock := &testmanifest.CachedManager{} + mock.OnAnything(cacheManagerMock, "Get").Return(nil, fmt.Errorf("unable to do stuff: %w", cache.ErrNotFound)) + regCliMock := ®testing.Client{} + mock.OnAnything(regCliMock, "PullManifest").Return(manifest, "", nil) + mock.OnAnything(cacheManagerMock, "Save").Return(nil) + + handler := &referrersHandler{ + artifactManager: artifactMock, + accessoryManager: accessoryMock, + registryClient: regCliMock, + maniCacheManager: cacheManagerMock, + } + + handler.ServeHTTP(rec, req) + + // check that the response has the expected status code (200 OK) + if rec.Code != http.StatusOK { + t.Errorf("Expected status code %d, but got %d", http.StatusOK, rec.Code) + } + index := &ocispec.Index{} + json.Unmarshal(rec.Body.Bytes(), index) + if index.Manifests[0].ArtifactType != "application/vnd.example.sbom" { + t.Errorf("Expected response body %s, but got %s", "application/vnd.example.sbom", rec.Body.String()) + } + _, content, _ := manifest.Payload() + assert.Equal(t, int64(len(content)), index.Manifests[0].Size) } func TestReferrersHandlerEmpty(t *testing.T) { From 2e7db335b3d082aa184f24203c284e21a4e00734 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Tue, 9 Apr 2024 17:30:53 +0800 Subject: [PATCH 006/100] Add auto generate SBOM on push feature (#20250) Signed-off-by: stonezdj Co-authored-by: stonezdj Co-authored-by: Wang Yan --- .../event/handler/internal/artifact.go | 5 ++++ src/controller/event/handler/internal/util.go | 19 ++++++++++++ .../event/handler/internal/util_test.go | 30 +++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/src/controller/event/handler/internal/artifact.go b/src/controller/event/handler/internal/artifact.go index fb212ac87..9218db95a 100644 --- a/src/controller/event/handler/internal/artifact.go +++ b/src/controller/event/handler/internal/artifact.go @@ -258,6 +258,11 @@ func (a *ArtifactEventHandler) onPush(ctx context.Context, event *event.Artifact if err := autoScan(ctx, &artifact.Artifact{Artifact: *event.Artifact}, event.Tags...); err != nil { log.Errorf("scan artifact %s@%s failed, error: %v", event.Artifact.RepositoryName, event.Artifact.Digest, err) } + + log.Debugf("auto generate sbom is triggered for artifact event %+v", event) + if err := autoGenSBOM(ctx, &artifact.Artifact{Artifact: *event.Artifact}); err != nil { + log.Errorf("generate sbom for artifact %s@%s failed, error: %v", event.Artifact.RepositoryName, event.Artifact.Digest, err) + } }() return nil diff --git a/src/controller/event/handler/internal/util.go b/src/controller/event/handler/internal/util.go index c7cf51243..cc10e09ca 100644 --- a/src/controller/event/handler/internal/util.go +++ b/src/controller/event/handler/internal/util.go @@ -20,6 +20,7 @@ import ( "github.com/goharbor/harbor/src/controller/artifact" "github.com/goharbor/harbor/src/controller/project" "github.com/goharbor/harbor/src/controller/scan" + "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" ) @@ -43,3 +44,21 @@ func autoScan(ctx context.Context, a *artifact.Artifact, tags ...string) error { return scan.DefaultController.Scan(ctx, a, options...) })(orm.SetTransactionOpNameToContext(ctx, "tx-auto-scan")) } + +func autoGenSBOM(ctx context.Context, a *artifact.Artifact) error { + proj, err := project.Ctl.Get(ctx, a.ProjectID) + if err != nil { + return err + } + if !proj.AutoSBOMGen() { + return nil + } + // transaction here to work with the image index + return orm.WithTransaction(func(ctx context.Context) error { + options := []scan.Option{} + // TODO: extract the sbom scan type to a constant + options = append(options, scan.WithScanType("sbom")) + log.Debugf("sbom scan controller artifact %+v, options %+v", a, options) + return scan.DefaultController.Scan(ctx, a, options...) + })(orm.SetTransactionOpNameToContext(ctx, "tx-auto-gen-sbom")) +} diff --git a/src/controller/event/handler/internal/util_test.go b/src/controller/event/handler/internal/util_test.go index 615fad9f6..4a48378a4 100644 --- a/src/controller/event/handler/internal/util_test.go +++ b/src/controller/event/handler/internal/util_test.go @@ -95,6 +95,36 @@ func (suite *AutoScanTestSuite) TestAutoScan() { suite.Nil(autoScan(ctx, art)) } +func (suite *AutoScanTestSuite) TestAutoScanSBOM() { + mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{ + Metadata: map[string]string{ + proModels.ProMetaAutoSBOMGen: "true", + }, + }, nil) + + mock.OnAnything(suite.scanController, "Scan").Return(nil) + + ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) + art := &artifact.Artifact{} + + suite.Nil(autoGenSBOM(ctx, art)) +} + +func (suite *AutoScanTestSuite) TestAutoScanSBOMFalse() { + mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{ + Metadata: map[string]string{ + proModels.ProMetaAutoSBOMGen: "false", + }, + }, nil) + + mock.OnAnything(suite.scanController, "Scan").Return(nil) + + ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) + art := &artifact.Artifact{} + + suite.Nil(autoGenSBOM(ctx, art)) +} + func (suite *AutoScanTestSuite) TestAutoScanFailed() { mock.OnAnything(suite.projectController, "Get").Return(&proModels.Project{ Metadata: map[string]string{ From 2bb5166c80c6e315646ad77042d5f6637743d165 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Wed, 10 Apr 2024 13:46:00 +0800 Subject: [PATCH 007/100] adopt cosign with oci-spec 1.1 (#20245) Signed-off-by: yminer add comment for cosign middlware --- src/server/middleware/cosign/cosign.go | 32 ++++++++++++++++++++++++ src/server/middleware/subject/subject.go | 5 ++++ 2 files changed, 37 insertions(+) diff --git a/src/server/middleware/cosign/cosign.go b/src/server/middleware/cosign/cosign.go index 13021cb10..53fcdc7ed 100644 --- a/src/server/middleware/cosign/cosign.go +++ b/src/server/middleware/cosign/cosign.go @@ -65,6 +65,38 @@ var ( } ] } +*/ +// cosign adopt oci-spec 1.1 will have request and manifest like below +// It will skip this middleware since not using cosignRe for subject artifact reference +// use Subject Middleware indtead +/* +PUT /v2/library/goharbor/harbor-db/manifests/sha256:aabea2bdd5a6fb79c13837b88c7b158f4aa57a621194ee21959d0b520eda412f +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.dev.cosign.artifact.sig.v1+json", + "size": 233, + "digest": "sha256:c025e9532dbc880534be96dbbb86a6bf63a272faced7f07bb8b4ceb45ca938d1" + }, + "layers": [ + { + "mediaType": "application/vnd.dev.cosign.simplesigning.v1+json", + "size": 257, + "digest": "sha256:38d07d81bf1d026da6420295113115d999ad6da90073b5e67147f978626423e6", + "annotations": { + "dev.cosignproject.cosign/signature": "MEUCIDOQc6I4MSd4/s8Bc8S7LXHCOnm4MGimpQdeCInLzM0VAiEAhWWYxmwEmYrFJ8xYNE3ow7PS4zeGe1R4RUbXRIawKJ4=", + "dev.sigstore.cosign/bundle": "{\"SignedEntryTimestamp\":\"MEUCIC5DSFQx3nZhPFquF4NAdfetjqLR6qAa9i04cEtAg7VjAiEAzG2DUxqH+MdFSPih/EL/Vvsn3L1xCJUlOmRZeUYZaG0=\",\"Payload\":{\"body\":\"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIzOGQwN2Q4MWJmMWQwMjZkYTY0MjAyOTUxMTMxMTVkOTk5YWQ2ZGE5MDA3M2I1ZTY3MTQ3Zjk3ODYyNjQyM2U2In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJRE9RYzZJNE1TZDQvczhCYzhTN0xYSENPbm00TUdpbXBRZGVDSW5Mek0wVkFpRUFoV1dZeG13RW1ZckZKOHhZTkUzb3c3UFM0emVHZTFSNFJVYlhSSWF3S0o0PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGWVVoSk1DOTZiWEpIYW1VNE9FeFVTM0ZDU2tvNWJXZDNhWEprWkFwaVJrZGpNQzlRYWtWUUwxbFJNelJwZFZweWJGVnRhMGx3ZDBocFdVTmxSV3M0YWpoWE5rSnBaV3BxTHk5WmVVRnZZaXN5VTFCTGRqUkJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=\",\"integratedTime\":1712651102,\"logIndex\":84313668,\"logID\":\"c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d\"}}" + } + } + ], + "subject": { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "size": 2621, + "digest": "sha256:e50f88df1b11f94627e35bed9f34214392363508a2b07146d0a94516da97e4c0" + } +} + */ func SignatureMiddleware() func(http.Handler) http.Handler { return middleware.AfterResponse(func(w http.ResponseWriter, r *http.Request, statusCode int) error { diff --git a/src/server/middleware/subject/subject.go b/src/server/middleware/subject/subject.go index 4c1c47315..7995703e2 100644 --- a/src/server/middleware/subject/subject.go +++ b/src/server/middleware/subject/subject.go @@ -39,6 +39,9 @@ var ( // the media type of notation signature layer mediaTypeNotationLayer = "application/vnd.cncf.notary.signature" + // cosign media type in config layer, which would support in oci-spec1.1 + mediaTypeCosignConfig = "application/vnd.dev.cosign.artifact.sig.v1+json" + // annotation of nydus image layerAnnotationNydusBootstrap = "containerd.io/snapshot/nydus-bootstrap" @@ -152,6 +155,8 @@ func Middleware() func(http.Handler) http.Handler { } case mediaTypeNotationLayer: accData.Type = model.TypeNotationSignature + case mediaTypeCosignConfig: + accData.Type = model.TypeCosignSignature case mediaTypeHarborSBOM: accData.Type = model.TypeHarborSBOM } From a858fb4f4d90a31a50e728a40a89f29d184a6d55 Mon Sep 17 00:00:00 2001 From: tostt Date: Wed, 10 Apr 2024 09:17:30 +0200 Subject: [PATCH 008/100] Updated internationalisation : fr-fr (#20179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update french translation Signed-off-by: tostt * More updates french language Signed-off-by: tostt * Corr. spelling Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Florian Blampey Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Florian Blampey Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update fr-fr-lang.json : further changes following thcdrt's review Signed-off-by: tostt * Update src/portal/src/i18n/lang/fr-fr-lang.json Co-authored-by: Thomas Coudert Signed-off-by: tostt * Update fr-fr-lang.json: translate Expand to Déplier Signed-off-by: tostt * Update fr-fr-lang.json: Remove duplicate portion of text Signed-off-by: tostt --------- Signed-off-by: tostt Co-authored-by: Vadim Bauer Co-authored-by: Florian Blampey Co-authored-by: Thomas Coudert Co-authored-by: Wang Yan --- src/portal/src/i18n/lang/fr-fr-lang.json | 140 +++++++++++------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index f09c6ba66..bf45e5c53 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -5,7 +5,7 @@ "VIC": "vSphere Integrated Containers", "MGMT": "Management", "REG": "Registre", - "HARBOR_SWAGGER": "Harbor Swagger", + "HARBOR_SWAGGER": "Swagger Harbor", "THEME_DARK_TEXT": "SOMBRE", "THEME_LIGHT_TEXT": "CLAIR" }, @@ -28,7 +28,7 @@ "DELETE": "Supprimer", "LOG_IN": "S'identifier", "LOG_IN_OIDC": "Connexion via fournisseur OIDC", - "LOG_IN_OIDC_WITH_PROVIDER_NAME": "LOGIN WITH {{providerName}}", + "LOG_IN_OIDC_WITH_PROVIDER_NAME": "S'IDENTIFIER AVEC {{providerName}}", "SIGN_UP_LINK": "Ouvrir un compte", "SIGN_UP": "S'inscrire", "CONFIRM": "Confirmer", @@ -130,7 +130,7 @@ "ADMIN_RENAME_TIP": "Cliquez sur le bouton pour changer le nom d'utilisateur en \"admin@harbor.local\". Cette opération ne peut pas être annulée.", "RENAME_SUCCESS": "Renommage effectué !", "RENAME_CONFIRM_INFO": "Attention, changer le nom d'utilisateur pour \"admin@harbor.local\" ne peut pas être annulé.", - "CLI_PASSWORD": "CLI secret", + "CLI_PASSWORD": "Secret CLI", "CLI_PASSWORD_TIP": "Le secret CLI peut être utilisé comme mot de passe pour le client Docker ou Helm. Lorsque le mode d'authentification est OIDC, nous recommandons fortement d'utiliser des comptes robots, car les secrets CLI dépendent de la validité du jeton ID et nécessitent que l'utilisateur se connecte régulièrement à l'interface utilisateur pour rafraîchir le jeton.", "COPY_SUCCESS": "Copie effectuée", "COPY_ERROR": "Copie échouée", @@ -248,12 +248,12 @@ "INLINE_HELP_PUBLIC": "Lorsqu'un projet est mis en public, n'importe qui a l'autorisation de lire les dépôts sous ce projet, et l'utilisateur n'a pas besoin d'exécuter \"docker login\" avant de prendre des images de ce projet.", "OF": "sur", "COUNT_QUOTA": "Quota de nombre", - "STORAGE_QUOTA": "Project quota limits", + "STORAGE_QUOTA": "Quota du projet", "COUNT_QUOTA_TIP": "Entrez un entier entre '1' et '100000000', ou '-1' pour un quota illimité", "STORAGE_QUOTA_TIP": "La limite haute du quota de stockage n'accepte que des valeurs entières, au maximum '1024TB'. Entrez '-1' pour un quota illimité", - "QUOTA_UNLIMIT_TIP": "The maximum logical space that can be used by the project. Pour un quota illimité, entrez '-1'.", + "QUOTA_UNLIMIT_TIP": "Espace maximum logique pouvant être utilisé par le projet. Pour un quota illimité, entrez '-1'.", "TYPE": "Type", - "PROXY_CACHE": "Proxy Cache", + "PROXY_CACHE": "Cache proxy", "PROXY_CACHE_TOOLTIP": "Activez cette option pour permettre à ce projet d'agir comme un cache de pull pour un espace de noms particulier dans un registre cible. Harbor ne peut agir en tant que proxy que pour les registres DockerHub et Harbor.", "ENDPOINT": "Endpoint", "PROXY_CACHE_ENDPOINT": "Endpoint du Proxy Cache", @@ -287,9 +287,9 @@ "SCAN": "Analyse des vulnérabilités", "AUTOSCAN_TOGGLE": "Analyse automatique des images lors de l'envoi", "AUTOSCAN_POLICY": "Analyser automatiquement les images lorsqu'elles sont envoyées au projet du registre.", - "SBOM": "SBOM generation", - "AUTOSBOM_TOGGLE": "Automatically generate SBOM on push", - "AUTOSBOM_POLICY": "Automatically generate SBOM when the images are pushed to the project registry." + "SBOM": "Génération de SBOM", + "AUTOSBOM_TOGGLE": "Générer automatiquement un SBOM au push", + "AUTOSBOM_POLICY": "Générer automatiquement un SBOM lorsque les images sont poussées sur le registre." }, "MEMBER": { "NEW_USER": "Ajouter un nouveau membre", @@ -339,7 +339,7 @@ "SWITCH_TITLE": "Confirmez le changement de membres projet", "SWITCH_SUMMARY": "Voulez-vous changer les membres projet {{param}}?", "SET_ROLE": "Définir Role", - "REMOVE": "Remove", + "REMOVE": "Retirer", "GROUP_NAME_REQUIRED": "Le nom du groupe est requis", "NON_EXISTENT_GROUP": "Ce groupe n'existe pas", "GROUP_ALREADY_ADDED": "Ce groupe a déjà été ajouté au projet" @@ -383,46 +383,46 @@ "NEVER_EXPIRED": "Ne jamais expirer", "NAME_PREFIX": "Préfixe du nom du compte robot", "NAME_PREFIX_REQUIRED": "Le préfixe du nom du compte robot est obligatoire", - "UPDATE": "Update", - "AUDIT_LOG": "Audit Log", - "PREHEAT_INSTANCE": "Preheat Instance", - "PROJECT": "Project", - "REPLICATION_POLICY": "Replication Policy", - "REPLICATION": "Replication", - "REPLICATION_ADAPTER": "Replication Adapter", - "REGISTRY": "Registry", - "SCAN_ALL": "Scan All", - "SYSTEM_VOLUMES": "System Volumes", - "GARBAGE_COLLECTION": "Garbage Collection", - "PURGE_AUDIT": "Purge Audit", + "UPDATE": "Mettre à jour", + "AUDIT_LOG": "Log d'audit", + "PREHEAT_INSTANCE": "Préchauffer l'instance", + "PROJECT": "Projet", + "REPLICATION_POLICY": "Politique de réplication", + "REPLICATION": "Réplication", + "REPLICATION_ADAPTER": "Adaptateur de réplication", + "REGISTRY": "Registre", + "SCAN_ALL": "Scanner tout", + "SYSTEM_VOLUMES": "Volumes système", + "GARBAGE_COLLECTION": "Purge", + "PURGE_AUDIT": "Purger l'audit", "JOBSERVICE_MONITOR": "Job Service Monitor", - "TAG_RETENTION": "Tag Retention", + "TAG_RETENTION": "Rétention des tags", "SCANNER": "Scanner", "LABEL": "Label", - "EXPORT_CVE": "Export CVE", - "SECURITY_HUB": "Security Hub", - "CATALOG": "Catalog", - "METADATA": "Project Metadata", - "REPOSITORY": "Repository", - "ARTIFACT": "Artifact", + "EXPORT_CVE": "Exporter les CVE", + "SECURITY_HUB": "Centre de sécurité", + "CATALOG": "Catalogue", + "METADATA": "Métadonnées du projet", + "REPOSITORY": "Dépôt", + "ARTIFACT": "Artefact", "SCAN": "Scan", "TAG": "Tag", - "ACCESSORY": "Accessory", - "ARTIFACT_ADDITION": "Artifact Addition", - "ARTIFACT_LABEL": "Artifact Label", - "PREHEAT_POLICY": "Preheat Policy", - "IMMUTABLE_TAG": "Immutable Tag", + "ACCESSORY": "Accessoire", + "ARTIFACT_ADDITION": "Artefact Addition", + "ARTIFACT_LABEL": "Label d'artefact", + "PREHEAT_POLICY": "Politique de préchauffage", + "IMMUTABLE_TAG": "Tag immutable", "LOG": "Log", - "NOTIFICATION_POLICY": "Notification Policy", + "NOTIFICATION_POLICY": "Politique de notification", "QUOTA": "Quota", - "BACK": "Back", - "NEXT": "Next", - "FINISH": "Finish", - "BASIC_INFO": "Basic Information", - "SELECT_PERMISSIONS": "Select Permissions", - "SELECT_SYSTEM_PERMISSIONS": "Select System Permissions", - "SELECT_PROJECT_PERMISSIONS": "Select Project Permissions", - "SYSTEM_PERMISSIONS": "System Permissions" + "BACK": "Retour", + "NEXT": "Suivant", + "FINISH": "Finir", + "BASIC_INFO": "Informations de base", + "SELECT_PERMISSIONS": "Selectionner les permissions", + "SELECT_SYSTEM_PERMISSIONS": "Selectionner les permissions système", + "SELECT_PROJECT_PERMISSIONS": "Selectionner les permissions projet", + "SYSTEM_PERMISSIONS": "Permissions système" }, "WEBHOOK": { "EDIT_BUTTON": "Éditer", @@ -536,7 +536,7 @@ "RESOURCE_TYPE": "Type de ressource" }, "REPLICATION": { - "PUSH_BASED_ONLY": "Only for the push-based replication", + "PUSH_BASED_ONLY": "Uniquement pour la réplication de type push", "YES": "Oui", "SECONDS": "Secondes", "MINUTES": "Minutes", @@ -645,7 +645,7 @@ "CANNOT_EDIT": "La règle de réplication ne peut pas être modifiée lorsqu'elle est activée.", "INVALID_DATE": "Date non valide.", "PLACEHOLDER": "Nous n'avons trouvé aucune règle de réplication !", - "JOB_PLACEHOLDER": "Nous n'avons trouvé aucun travail de réplication !", + "JOB_PLACEHOLDER": "Nous n'avons trouvé aucune tâche de réplication !", "NO_ENDPOINT_INFO": "Ajoutez d'abord un endpoint", "NO_LABEL_INFO": "Ajoutez d'abord un label", "NO_PROJECT_INFO": "Ce projet n'existe pas", @@ -705,11 +705,11 @@ "BANDWIDTH_TOOLTIP": "Set the maximum network bandwidth for each replication worker. Please pay attention to the number of concurrent executions (max. {{max_job_workers}}). For unlimited bandwidth, please enter -1.", "UNLIMITED": "Illimitée", "UNREACHABLE_SOURCE_REGISTRY": "Échec de connexion au registre source. Veuillez vérifier que le registre source est disponible avant d'éditer cette règle: {{error}}", - "CRON_ERROR_TIP": "The 1st field of the cron string must be 0 and the 2nd filed can not be \"*\"", + "CRON_ERROR_TIP": "Le 1er champ de la chaîne cron doit être 0 et le 2ème champ ne peut pas être \"*\"", "COPY_BY_CHUNK": "Copier par morceaux", "COPY_BY_CHUNK_TIP": "Spécifie si le blob doit être copié par morceaux. Transférer par morceaux peut augmenter le nombre de requêtes faites à l'API.", "TRIGGER_STOP_SUCCESS": "Déclenchement avec succès de l'arrêt d'exécution", - "CRON_STR": "Cron String" + "CRON_STR": "Chaîne cron" }, "DESTINATION": { "NEW_ENDPOINT": "Nouveau Endpoint", @@ -938,7 +938,7 @@ "TOKEN_REVIEW": "Endpoint de revue de token", "SKIP_SEARCH": "Passer la recherche", "VERIFY_CERT": "Vérifier le certificat", - "ADMIN_GROUPS": "Admin Groups" + "ADMIN_GROUPS": "Groupes admin" }, "OIDC": { "OIDC_PROVIDER": "Fournisseur OIDC", @@ -1079,7 +1079,7 @@ "COLUMN_VERSION": "Version Actuelle", "COLUMN_FIXED": "Réparé dans la version", "COLUMN_DESCRIPTION": "Description", - "FOOT_ITEMS": "Items", + "FOOT_ITEMS": "Entrées", "FOOT_OF": "sur", "IN_ALLOW_LIST": "Présent dans la liste blanche CVE", "CVSS3": "CVSS3" @@ -1126,7 +1126,7 @@ "TAG_COMMAND": "Taguer une image pour ce projet :", "PUSH_COMMAND": "Push une image dans ce projet :", "COPY_ERROR": "Copie échouée, veuillez essayer de copier manuellement les commandes de référence.", - "COPY_PULL_COMMAND": "COPY PULL COMMAND" + "COPY_PULL_COMMAND": "COMMANDE COPY PULL" }, "ARTIFACT": { "FILTER_FOR_ARTIFACTS": "Filtrer les artefacts", @@ -1181,7 +1181,7 @@ "PULL_TIME": "Date/Heure de pull", "PUSH_TIME": "Date/Heure de push", "OF": "sur", - "ITEMS": "items", + "ITEMS": "entrées", "ADD_TAG": "AJOUTER TAG", "REMOVE_TAG": "SUPPRIMER TAG", "NAME_ALREADY_EXISTS": "Ce tag existe déjà dans ce dépôt" @@ -1211,14 +1211,14 @@ "DELETE": "Supprimer", "OF": "sur", "PROJECT_QUOTA_DEFAULT_ARTIFACT": "Nombre par défaut d'artefacts par projet", - "PROJECT_QUOTA_DEFAULT_DISK": "Default quota space per project", + "PROJECT_QUOTA_DEFAULT_DISK": "Quota d'espace par défaut par projet", "EDIT_PROJECT_QUOTAS": "Éditer les quotas projet", "EDIT_DEFAULT_PROJECT_QUOTAS": "Éditer les quotas projet par défaut", "SET_QUOTAS": "Configurer les quotas pour le projet '{{params}}'", "SET_DEFAULT_QUOTAS": "Configurer les quotas projet par défaut lors de la création de nouveaux projets", "COUNT_QUOTA": "Quota de nombre", "COUNT_DEFAULT_QUOTA": "Quota de nombre par défaut", - "STORAGE_QUOTA": "Project quota limits", + "STORAGE_QUOTA": "Limites des quotas de projets", "STORAGE_DEFAULT_QUOTA": "Quota de stockage par défaut", "SAVE_SUCCESS": "Edition de quota effectuée", "UNLIMITED": "Illimité", @@ -1469,9 +1469,9 @@ "NAME_REX": "Le nom doit comporter au moins 2 caractères avec des minuscules, des chiffres et. _- et doit commencer par des caractères ou des chiffres.", "DESCRIPTION": "Description", "SBOM": "SBOM", - "VULNERABILITY": "Vulnerability", - "SUPPORTED": "Supported", - "NOT_SUPPORTED": "Not Supported", + "VULNERABILITY": "Vulnérabilité", + "SUPPORTED": "Supporté", + "NOT_SUPPORTED": "Non Supporté", "ENDPOINT": "Endpoint", "ENDPOINT_EXISTS": "L'URL de l'endpoint existe déjà", "ENDPOINT_REQUIRED": "L'URL de l'endpoint est requise", @@ -1676,7 +1676,7 @@ "PREHEAT_EXPLAIN": "Le préchauffage migrera l'image vers le réseau p2p", "CRITERIA_EXPLAIN": "Comme spécifié dans la section 'Sécurité de déploiement' dans l'onglet Configuration", "SKIP_CERT_VERIFY": "Cochez cette case pour ignorer la vérification du certificat lorsque le fournisseur distant utilise un certificat auto-signé ou non approuvé.", - "NAME_TOOLTIP": "Policy name consists of one or more groups of uppercase letter, lowercase letter or number; and groups are separated by a dot, underscore, or hyphen.", + "NAME_TOOLTIP": "Le nom de la politique consiste en un ou plusieurs groupes de lettres (majuscules ou minuscules) ou de chiffres ; les groupes sont séparés par un point, un trait de soulignement ou un trait d'union.", "NEED_HELP": "Veuillez d'abord demander à votre administrateur système d'ajouter un fournisseur" }, "PAGINATION": { @@ -1712,9 +1712,9 @@ "PROJECTS_MODAL_TITLE": "Projets pour le compte robot", "PROJECTS_MODAL_SUMMARY": "Voici les projets couverts par ce compte robot.", "CREATE_ROBOT": "Créer un compte robot Système", - "CREATE_ROBOT_SUMMARY": "Create a system Robot Account that will cover permissions for the system as well as for specific projects", + "CREATE_ROBOT_SUMMARY": "Créer un compte système Robot qui couvrira les autorisations pour le système ainsi que pour des projets spécifiques", "EDIT_ROBOT": "Éditer un compte robot Système", - "EDIT_ROBOT_SUMMARY": "Edit a system Robot Account that will cover permissions for the system as well as for specific projects", + "EDIT_ROBOT_SUMMARY": "Éditer un compte système Robot qui couvrira les autorisations pour le système ainsi que pour des projets spécifiques", "EXPIRATION_TIME": "Date/Heure d'Expiration", "EXPIRATION_TIME_EXPLAIN": "L'heure d'expiration (en jours, le point de départ est l'heure de création) du jeton du compte robot. Pour ne jamais expirer, entrer \"-1\".", "EXPIRATION_DEFAULT": "jours (défaut)", @@ -1724,7 +1724,7 @@ "COVER_ALL": "Couvrir tous les projets", "COVER_ALL_EXPLAIN": "Cocher pour appliquer à tous les projets existants et futurs", "COVER_ALL_SUMMARY": "\"Tous les projets existants et futurs\" sélectionné.", - "RESET_PERMISSION": "RESET ALL PROJECT PERMISSIONS", + "RESET_PERMISSION": "REINITIALISER TOUTES LES PERMISSIONS PROJET", "PERMISSION_COLUMN": "Permissions", "EXPIRES_AT": "Expire à", "VIEW_SECRET": "Actualiser le secret", @@ -1757,8 +1757,8 @@ "REPOSITORY": "Dépôt", "EXPIRES_IN": "Expire dans", "EXPIRED": "Expiré", - "SELECT_ALL_PROJECT": "SELECT ALL PROJECTS", - "UNSELECT_ALL_PROJECT": "UNSELECT ALL PROJECTS" + "SELECT_ALL_PROJECT": "SELECTIONNER TOUS LES PROJETS", + "UNSELECT_ALL_PROJECT": "DESELECTIONNER TOUS LES PROJETS" }, "ACCESSORY": { "DELETION_TITLE_ACCESSORY": "Confirmer la suppression de l'accessoire", @@ -1809,7 +1809,7 @@ "EXPORT_TITLE": "Export de CVEs", "EXPORT_SUBTITLE": "Définir les conditions d'exportation", "EXPORT_CVE_FILTER_HELP_TEXT": "Entrer plusieurs cveIDs séparés par des virgules", - "CVE_IDS": "CVE IDs", + "CVE_IDS": "IDs CVE", "EXPORT_BUTTON": "Exporter", "JOB_NAME": "Nom de la tâche", "JOB_NAME_REQUIRED": "Le nom de la tâche est requis", @@ -1892,9 +1892,9 @@ "SCHEDULE_RESUME_BTN_INFO": "REPRENDRE — Reprend les files d'attente de tâches à exécuter.", "WORKER_FREE_BTN_INFO": "Arrête les tâches en cours pour libérer le worker", "CRON": "Cron", - "WAITING_TOO_LONG_1": "Certain jobs have been pending for execution for over 24 hours. Please check the job service ", - "WAITING_TOO_LONG_2": "dashboard.", - "WAITING_TOO_LONG_3": "For more details, please refer to the ", + "WAITING_TOO_LONG_1": "Certaines tâches sont en attente d'exécution depuis plus de 24 heures. Veuillez vérifier le ", + "WAITING_TOO_LONG_2": "tableau de bord.", + "WAITING_TOO_LONG_3": "Pour plus de détails, veuillez consulter le ", "WAITING_TOO_LONG_4": "Wiki." }, "CLARITY": { @@ -1902,8 +1902,8 @@ "CLOSE": "Fermer", "SHOW": "Afficher", "HIDE": "Cacher", - "EXPAND": "Etendre", - "COLLAPSE": "Collapse", + "EXPAND": "Déplier", + "COLLAPSE": "Replier", "MORE": "Plus", "SELECT": "Sélectionner", "SELECT_ALL": "Tout sélectionner", @@ -1960,7 +1960,7 @@ "ENTER_MESSAGE": "Entrer votre message ici" }, "SECURITY_HUB": { - "SECURITY_HUB": "Tableau de bord de sécurité", + "SECURITY_HUB": "Centre de sécurité", "ARTIFACTS": "artefact(s)", "SCANNED": "scannés", "NOT_SCANNED": "non scannés", @@ -1968,7 +1968,7 @@ "TOTAL_AND_FIXABLE": "{{totalNum}} total dont {{fixableNum}} corrigeables", "TOP_5_ARTIFACT": "Top 5 des Artefacts les plus Dangereux", "TOP_5_CVE": "Top 5 des CVEs les plus Dangereuses", - "CVE_ID": "CVE ID", + "CVE_ID": "ID CVE", "VUL": "Vulnérabilités", "CVE": "CVEs", "FILTER_BY": "Filtrer par", From 89995075a7f00d906f22be3f70b0474d82237d62 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 10 Apr 2024 20:39:25 +0800 Subject: [PATCH 009/100] Update swagger API to display SBOM content in addition API (#20234) complete task #20066 Signed-off-by: stonezdj Co-authored-by: stonezdj --- .../artifact/processor/chart/chart.go | 2 +- .../artifact/processor/sbom/sbom.go | 89 ++++++++++ .../artifact/processor/sbom/sbom_test.go | 166 ++++++++++++++++++ 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 src/controller/artifact/processor/sbom/sbom.go create mode 100644 src/controller/artifact/processor/sbom/sbom_test.go diff --git a/src/controller/artifact/processor/chart/chart.go b/src/controller/artifact/processor/chart/chart.go index 059d47bbf..e7df72b56 100644 --- a/src/controller/artifact/processor/chart/chart.go +++ b/src/controller/artifact/processor/chart/chart.go @@ -85,11 +85,11 @@ func (p *processor) AbstractAddition(_ context.Context, artifact *artifact.Artif if err != nil { return nil, err } + defer blob.Close() content, err := io.ReadAll(blob) if err != nil { return nil, err } - blob.Close() chartDetails, err := p.chartOperator.GetDetails(content) if err != nil { return nil, err diff --git a/src/controller/artifact/processor/sbom/sbom.go b/src/controller/artifact/processor/sbom/sbom.go new file mode 100644 index 000000000..ec0222fb9 --- /dev/null +++ b/src/controller/artifact/processor/sbom/sbom.go @@ -0,0 +1,89 @@ +// 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 sbom + +import ( + "context" + "encoding/json" + "io" + + v1 "github.com/opencontainers/image-spec/specs-go/v1" + + "github.com/goharbor/harbor/src/controller/artifact/processor" + "github.com/goharbor/harbor/src/controller/artifact/processor/base" + "github.com/goharbor/harbor/src/lib/errors" + "github.com/goharbor/harbor/src/lib/log" + "github.com/goharbor/harbor/src/pkg/artifact" +) + +const ( + // processorArtifactTypeSBOM is the artifact type for SBOM, it's scope is only used in the processor + processorArtifactTypeSBOM = "SBOM" + // processorMediaType is the media type for SBOM, it's scope is only used to register the processor + processorMediaType = "application/vnd.goharbor.harbor.sbom.v1" +) + +func init() { + pc := &Processor{} + pc.ManifestProcessor = base.NewManifestProcessor() + if err := processor.Register(pc, processorMediaType); err != nil { + log.Errorf("failed to register processor for media type %s: %v", processorMediaType, err) + return + } +} + +// Processor is the processor for SBOM +type Processor struct { + *base.ManifestProcessor +} + +// AbstractAddition returns the addition for SBOM +func (m *Processor) AbstractAddition(_ context.Context, art *artifact.Artifact, _ string) (*processor.Addition, error) { + man, _, err := m.RegCli.PullManifest(art.RepositoryName, art.Digest) + if err != nil { + return nil, errors.Wrap(err, "failed to pull manifest") + } + _, payload, err := man.Payload() + if err != nil { + return nil, errors.Wrap(err, "failed to get payload") + } + manifest := &v1.Manifest{} + if err := json.Unmarshal(payload, manifest); err != nil { + return nil, err + } + // SBOM artifact should only have one layer + if len(manifest.Layers) != 1 { + return nil, errors.New(nil).WithCode(errors.NotFoundCode).WithMessage("The sbom is not found") + } + layerDgst := manifest.Layers[0].Digest.String() + _, blob, err := m.RegCli.PullBlob(art.RepositoryName, layerDgst) + if err != nil { + return nil, errors.Wrap(err, "failed to pull the blob") + } + defer blob.Close() + content, err := io.ReadAll(blob) + if err != nil { + return nil, err + } + return &processor.Addition{ + Content: content, + ContentType: processorMediaType, + }, nil +} + +// GetArtifactType the artifact type is used to display the artifact type in the UI +func (m *Processor) GetArtifactType(_ context.Context, _ *artifact.Artifact) string { + return processorArtifactTypeSBOM +} diff --git a/src/controller/artifact/processor/sbom/sbom_test.go b/src/controller/artifact/processor/sbom/sbom_test.go new file mode 100644 index 000000000..6128c550f --- /dev/null +++ b/src/controller/artifact/processor/sbom/sbom_test.go @@ -0,0 +1,166 @@ +// 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 sbom + +import ( + "context" + "fmt" + "io" + "strings" + "testing" + + "github.com/docker/distribution" + v1 "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" + + "github.com/goharbor/harbor/src/controller/artifact/processor/base" + "github.com/goharbor/harbor/src/lib/errors" + "github.com/goharbor/harbor/src/pkg/artifact" + "github.com/goharbor/harbor/src/testing/pkg/registry" +) + +type SBOMProcessorTestSuite struct { + suite.Suite + processor *Processor + regCli *registry.Client +} + +func (suite *SBOMProcessorTestSuite) SetupSuite() { + suite.regCli = ®istry.Client{} + suite.processor = &Processor{ + &base.ManifestProcessor{ + RegCli: suite.regCli, + }, + } +} + +func (suite *SBOMProcessorTestSuite) TearDownSuite() { +} + +func (suite *SBOMProcessorTestSuite) TestAbstractAdditionNormal() { + manContent := `{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b", + "size": 498 + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 32654, + "digest": "sha256:abc" + }] +}` + sbomContent := "this is a sbom content" + reader := strings.NewReader(sbomContent) + blobReader := io.NopCloser(reader) + mani, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(manContent)) + suite.Require().NoError(err) + suite.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(mani, "sha256:123", nil).Once() + suite.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(123), blobReader, nil).Once() + addition, err := suite.processor.AbstractAddition(context.Background(), &artifact.Artifact{RepositoryName: "repo", Digest: "digest"}, "sbom") + suite.Nil(err) + suite.Equal(sbomContent, string(addition.Content)) +} + +func (suite *SBOMProcessorTestSuite) TestAbstractAdditionMultiLayer() { + manContent := `{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b", + "size": 498 + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 32654, + "digest": "sha256:abc" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 843, + "digest": "sha256:def" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 531, + "digest": "sha256:123" + } + ] +}` + mani, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(manContent)) + suite.Require().NoError(err) + suite.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(mani, "sha256:123", nil).Once() + _, err = suite.processor.AbstractAddition(context.Background(), &artifact.Artifact{RepositoryName: "repo", Digest: "digest"}, "sbom") + suite.NotNil(err) +} + +func (suite *SBOMProcessorTestSuite) TestAbstractAdditionPullBlobError() { + manContent := `{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b", + "size": 498 + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 32654, + "digest": "sha256:abc" + } + ] +}` + mani, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(manContent)) + suite.Require().NoError(err) + suite.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(mani, "sha256:123", nil).Once() + suite.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(123), nil, errors.NotFoundError(fmt.Errorf("not found"))).Once() + addition, err := suite.processor.AbstractAddition(context.Background(), &artifact.Artifact{RepositoryName: "repo", Digest: "digest"}, "sbom") + suite.NotNil(err) + suite.Nil(addition) +} +func (suite *SBOMProcessorTestSuite) TestAbstractAdditionNoSBOMLayer() { + manContent := `{ + "schemaVersion": 2, + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:e91b9dfcbbb3b88bac94726f276b89de46e4460b55f6e6d6f876e666b150ec5b", + "size": 498 + } +}` + mani, _, err := distribution.UnmarshalManifest(v1.MediaTypeImageManifest, []byte(manContent)) + suite.Require().NoError(err) + suite.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(mani, "sha256:123", nil).Once() + _, err = suite.processor.AbstractAddition(context.Background(), &artifact.Artifact{RepositoryName: "repo", Digest: "digest"}, "sbom") + suite.NotNil(err) +} + +func (suite *SBOMProcessorTestSuite) TestAbstractAdditionPullManifestError() { + suite.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(nil, "sha256:123", errors.NotFoundError(fmt.Errorf("not found"))).Once() + _, err := suite.processor.AbstractAddition(context.Background(), &artifact.Artifact{RepositoryName: "repo", Digest: "digest"}, "sbom") + suite.NotNil(err) + +} + +func (suite *SBOMProcessorTestSuite) TestGetArtifactType() { + suite.Equal(processorArtifactTypeSBOM, suite.processor.GetArtifactType(context.Background(), &artifact.Artifact{})) +} + +func TestSBOMProcessorTestSuite(t *testing.T) { + suite.Run(t, &SBOMProcessorTestSuite{}) +} From 5d7c668028ac11b64152259736b6251c49f26120 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 10 Apr 2024 22:47:45 +0800 Subject: [PATCH 010/100] Support list artifact with_sbom_overview option (#20244) Signed-off-by: stonezdj Co-authored-by: stonezdj --- src/controller/scan/base_controller.go | 38 +++++++++++++- src/controller/scan/base_controller_test.go | 37 +++++++++++++- src/pkg/scan/rest/v1/models.go | 22 +++++--- src/pkg/scan/rest/v1/models_test.go | 15 ++++++ src/pkg/scan/rest/v1/spec.go | 5 ++ src/server/v2.0/handler/artifact.go | 7 +-- .../handler/assembler/{vul.go => report.go} | 51 +++++++++++++------ .../assembler/{vul_test.go => report_test.go} | 45 ++++++++++++---- src/server/v2.0/handler/model/artifact.go | 14 +++++ src/server/v2.0/handler/model/option.go | 47 +++++++++++++++++ src/server/v2.0/handler/model/option_test.go | 33 ++++++++++++ 11 files changed, 277 insertions(+), 37 deletions(-) create mode 100644 src/pkg/scan/rest/v1/models_test.go rename src/server/v2.0/handler/assembler/{vul.go => report.go} (55%) rename src/server/v2.0/handler/assembler/{vul_test.go => report_test.go} (61%) create mode 100644 src/server/v2.0/handler/model/option.go create mode 100644 src/server/v2.0/handler/model/option_test.go diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index a57d1f21b..a1627af30 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -17,6 +17,7 @@ package scan import ( "bytes" "context" + "encoding/json" "fmt" "reflect" "strings" @@ -674,12 +675,23 @@ func (bc *basicController) GetReport(ctx context.Context, artifact *ar.Artifact, return reports, nil } +func isSBOMMimeTypes(mimeTypes []string) bool { + for _, mimeType := range mimeTypes { + if mimeType == v1.MimeTypeSBOMReport { + return true + } + } + return false +} + // GetSummary ... func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact, mimeTypes []string) (map[string]interface{}, error) { if artifact == nil { return nil, errors.New("no way to get report summaries for nil artifact") } - + if isSBOMMimeTypes(mimeTypes) { + return bc.GetSBOMSummary(ctx, artifact, mimeTypes) + } // Get reports first rps, err := bc.GetReport(ctx, artifact, mimeTypes) if err != nil { @@ -708,6 +720,30 @@ func (bc *basicController) GetSummary(ctx context.Context, artifact *ar.Artifact return summaries, nil } +func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, mimeTypes []string) (map[string]interface{}, error) { + if art == nil { + return nil, errors.New("no way to get report summaries for nil artifact") + } + r, err := bc.sc.GetRegistrationByProject(ctx, art.ProjectID) + if err != nil { + return nil, errors.Wrap(err, "scan controller: get sbom summary") + } + reports, err := bc.manager.GetBy(ctx, art.Digest, r.UUID, mimeTypes) + if err != nil { + return nil, err + } + if len(reports) == 0 { + return map[string]interface{}{}, nil + } + reportContent := reports[0].Report + if len(reportContent) == 0 { + log.Warning("no content for current report") + } + result := map[string]interface{}{} + err = json.Unmarshal([]byte(reportContent), &result) + return result, err +} + // GetScanLog ... func (bc *basicController) GetScanLog(ctx context.Context, artifact *ar.Artifact, uuid string) ([]byte, error) { if len(uuid) == 0 { diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index 9d9f2c334..9d485f16c 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -78,7 +78,7 @@ type ControllerTestSuite struct { taskMgr *tasktesting.Manager reportMgr *reporttesting.Manager ar artifact.Controller - c Controller + c *basicController reportConverter *postprocessorstesting.ScanReportV1ToV2Converter cache *mockcache.Cache } @@ -180,7 +180,19 @@ func (suite *ControllerTestSuite) SetupSuite() { }, } + sbomReport := []*scan.Report{ + { + ID: 12, + UUID: "rp-uuid-002", + Digest: "digest-code", + RegistrationUUID: "uuid001", + MimeType: "application/vnd.scanner.adapter.sbom.report.harbor+json; version=1.0", + Status: "Success", + Report: `{"sbom_digest": "sha256:1234567890", "scan_status": "Success", "duration": 3, "start_time": "2021-09-01T00:00:00Z", "end_time": "2021-09-01T00:00:03Z"}`, + }, + } mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeNativeReport}).Return(reports, nil) + mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeSBOMReport}).Return(sbomReport, nil) mgr.On("Get", mock.Anything, "rp-uuid-001").Return(reports[0], nil) mgr.On("UpdateReportData", "rp-uuid-001", suite.rawReport, (int64)(10000)).Return(nil) mgr.On("UpdateStatus", "the-uuid-123", "Success", (int64)(10000)).Return(nil) @@ -620,3 +632,26 @@ func (suite *ControllerTestSuite) makeExtraAttrs(artifactID int64, reportUUIDs . return extraAttrs } + +func (suite *ControllerTestSuite) TestGenerateSBOMSummary() { + sum, err := suite.c.GetSBOMSummary(context.TODO(), suite.artifact, []string{v1.MimeTypeSBOMReport}) + suite.Nil(err) + suite.NotNil(sum) + status := sum["scan_status"] + suite.NotNil(status) + dgst := sum["sbom_digest"] + suite.NotNil(dgst) + suite.Equal("Success", status) + suite.Equal("sha256:1234567890", dgst) +} + +func TestIsSBOMMimeTypes(t *testing.T) { + // Test with a slice containing the SBOM mime type + assert.True(t, isSBOMMimeTypes([]string{v1.MimeTypeSBOMReport})) + + // Test with a slice not containing the SBOM mime type + assert.False(t, isSBOMMimeTypes([]string{"application/vnd.oci.image.manifest.v1+json"})) + + // Test with an empty slice + assert.False(t, isSBOMMimeTypes([]string{})) +} diff --git a/src/pkg/scan/rest/v1/models.go b/src/pkg/scan/rest/v1/models.go index fc48717fb..c31edb93b 100644 --- a/src/pkg/scan/rest/v1/models.go +++ b/src/pkg/scan/rest/v1/models.go @@ -21,12 +21,11 @@ import ( "github.com/goharbor/harbor/src/lib/errors" ) -const ( - // ScanTypeVulnerability the scan type for vulnerability - ScanTypeVulnerability = "vulnerability" - // ScanTypeSbom the scan type for sbom - ScanTypeSbom = "sbom" -) +var supportedMimeTypes = []string{ + MimeTypeNativeReport, + MimeTypeGenericVulnerabilityReport, + MimeTypeSBOMReport, +} // Scanner represents metadata of a Scanner Adapter which allow Harbor to lookup a scanner capable of // scanning a given Artifact stored in its registry and making sure that it can interpret a @@ -105,7 +104,7 @@ func (md *ScannerAdapterMetadata) Validate() error { // either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required found = false for _, pm := range ca.ProducesMimeTypes { - if pm == MimeTypeNativeReport || pm == MimeTypeGenericVulnerabilityReport { + if isSupportedMimeType(pm) { found = true break } @@ -119,6 +118,15 @@ func (md *ScannerAdapterMetadata) Validate() error { return nil } +func isSupportedMimeType(mimeType string) bool { + for _, mt := range supportedMimeTypes { + if mt == mimeType { + return true + } + } + return false +} + // HasCapability returns true when mine type of the artifact support by the scanner func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool { for _, capability := range md.Capabilities { diff --git a/src/pkg/scan/rest/v1/models_test.go b/src/pkg/scan/rest/v1/models_test.go new file mode 100644 index 000000000..e96aa0178 --- /dev/null +++ b/src/pkg/scan/rest/v1/models_test.go @@ -0,0 +1,15 @@ +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsSupportedMimeType(t *testing.T) { + // Test with a supported mime type + assert.True(t, isSupportedMimeType(MimeTypeSBOMReport), "isSupportedMimeType should return true for supported mime types") + + // Test with an unsupported mime type + assert.False(t, isSupportedMimeType("unsupported/mime-type"), "isSupportedMimeType should return false for unsupported mime types") +} diff --git a/src/pkg/scan/rest/v1/spec.go b/src/pkg/scan/rest/v1/spec.go index bb46d1c1b..a867e7167 100644 --- a/src/pkg/scan/rest/v1/spec.go +++ b/src/pkg/scan/rest/v1/spec.go @@ -39,9 +39,14 @@ const ( MimeTypeScanRequest = "application/vnd.scanner.adapter.scan.request+json; version=1.0" // MimeTypeScanResponse defines the mime type for scan response MimeTypeScanResponse = "application/vnd.scanner.adapter.scan.response+json; version=1.0" + // MimeTypeSBOMReport + MimeTypeSBOMReport = "application/vnd.security.sbom.report+json; version=1.0" // MimeTypeGenericVulnerabilityReport defines the MIME type for the generic report with enhanced information MimeTypeGenericVulnerabilityReport = "application/vnd.security.vulnerability.report; version=1.1" + ScanTypeVulnerability = "vulnerability" + ScanTypeSbom = "sbom" + apiPrefix = "/api/v1" ) diff --git a/src/server/v2.0/handler/artifact.go b/src/server/v2.0/handler/artifact.go index 84d78cc5d..dfc457047 100644 --- a/src/server/v2.0/handler/artifact.go +++ b/src/server/v2.0/handler/artifact.go @@ -107,8 +107,8 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr if err != nil { return a.SendError(ctx, err) } - - assembler := assembler.NewVulAssembler(lib.BoolValue(params.WithScanOverview), parseScanReportMimeTypes(params.XAcceptVulnerabilities)) + overviewOpts := model.NewOverviewOptions(model.WithSBOM(lib.BoolValue(params.WithSbomOverview)), model.WithVuln(lib.BoolValue(params.WithScanOverview))) + assembler := assembler.NewScanReportAssembler(overviewOpts, parseScanReportMimeTypes(params.XAcceptVulnerabilities)) var artifacts []*models.Artifact for _, art := range arts { artifact := &model.Artifact{} @@ -138,8 +138,9 @@ func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtif } art := &model.Artifact{} art.Artifact = *artifact + overviewOpts := model.NewOverviewOptions(model.WithSBOM(lib.BoolValue(params.WithSbomOverview)), model.WithVuln(lib.BoolValue(params.WithScanOverview))) - err = assembler.NewVulAssembler(lib.BoolValue(params.WithScanOverview), parseScanReportMimeTypes(params.XAcceptVulnerabilities)).WithArtifacts(art).Assemble(ctx) + err = assembler.NewScanReportAssembler(overviewOpts, parseScanReportMimeTypes(params.XAcceptVulnerabilities)).WithArtifacts(art).Assemble(ctx) if err != nil { log.Warningf("failed to assemble vulnerabilities with artifact, error: %v", err) } diff --git a/src/server/v2.0/handler/assembler/vul.go b/src/server/v2.0/handler/assembler/report.go similarity index 55% rename from src/server/v2.0/handler/assembler/vul.go rename to src/server/v2.0/handler/assembler/report.go index 055baab35..e4f9657ea 100644 --- a/src/server/v2.0/handler/assembler/vul.go +++ b/src/server/v2.0/handler/assembler/report.go @@ -20,43 +20,48 @@ import ( "github.com/goharbor/harbor/src/controller/scan" "github.com/goharbor/harbor/src/lib" "github.com/goharbor/harbor/src/lib/log" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" "github.com/goharbor/harbor/src/server/v2.0/handler/model" ) const ( vulnerabilitiesAddition = "vulnerabilities" + startTime = "start_time" + endTime = "end_time" + scanStatus = "scan_status" + sbomDigest = "sbom_digest" + duration = "duration" ) -// NewVulAssembler returns vul assembler -func NewVulAssembler(withScanOverview bool, mimeTypes []string) *VulAssembler { - return &VulAssembler{ - scanChecker: scan.NewChecker(), - scanCtl: scan.DefaultController, - - withScanOverview: withScanOverview, - mimeTypes: mimeTypes, +// NewScanReportAssembler returns vul assembler +func NewScanReportAssembler(option *model.OverviewOptions, mimeTypes []string) *ScanReportAssembler { + return &ScanReportAssembler{ + overviewOption: option, + scanChecker: scan.NewChecker(), + scanCtl: scan.DefaultController, + mimeTypes: mimeTypes, } } -// VulAssembler vul assembler -type VulAssembler struct { +// ScanReportAssembler vul assembler +type ScanReportAssembler struct { scanChecker scan.Checker scanCtl scan.Controller - artifacts []*model.Artifact - withScanOverview bool - mimeTypes []string + artifacts []*model.Artifact + mimeTypes []string + overviewOption *model.OverviewOptions } // WithArtifacts set artifacts for the assembler -func (assembler *VulAssembler) WithArtifacts(artifacts ...*model.Artifact) *VulAssembler { +func (assembler *ScanReportAssembler) WithArtifacts(artifacts ...*model.Artifact) *ScanReportAssembler { assembler.artifacts = artifacts return assembler } // Assemble assemble vul for the artifacts -func (assembler *VulAssembler) Assemble(ctx context.Context) error { +func (assembler *ScanReportAssembler) Assemble(ctx context.Context) error { version := lib.GetAPIVersion(ctx) for _, artifact := range assembler.artifacts { @@ -72,7 +77,7 @@ func (assembler *VulAssembler) Assemble(ctx context.Context) error { artifact.SetAdditionLink(vulnerabilitiesAddition, version) - if assembler.withScanOverview { + if assembler.overviewOption.WithVuln { for _, mimeType := range assembler.mimeTypes { overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{mimeType}) if err != nil { @@ -83,6 +88,20 @@ func (assembler *VulAssembler) Assemble(ctx context.Context) error { } } } + if assembler.overviewOption.WithSBOM { + overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{v1.MimeTypeSBOMReport}) + if err != nil { + log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, v1.MimeTypeSBOMReport, err) + } else if len(overview) > 0 { + artifact.SBOMOverView = map[string]interface{}{ + startTime: overview[startTime], + endTime: overview[endTime], + scanStatus: overview[scanStatus], + sbomDigest: overview[sbomDigest], + duration: overview[duration], + } + } + } } return nil diff --git a/src/server/v2.0/handler/assembler/vul_test.go b/src/server/v2.0/handler/assembler/report_test.go similarity index 61% rename from src/server/v2.0/handler/assembler/vul_test.go rename to src/server/v2.0/handler/assembler/report_test.go index 202720afa..6079a1d60 100644 --- a/src/server/v2.0/handler/assembler/vul_test.go +++ b/src/server/v2.0/handler/assembler/report_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/suite" + v1sq "github.com/goharbor/harbor/src/pkg/scan/rest/v1" "github.com/goharbor/harbor/src/server/v2.0/handler/model" "github.com/goharbor/harbor/src/testing/controller/scan" "github.com/goharbor/harbor/src/testing/mock" @@ -33,11 +34,11 @@ func (suite *VulAssemblerTestSuite) TestScannable() { checker := &scan.Checker{} scanCtl := &scan.Controller{} - assembler := VulAssembler{ - scanChecker: checker, - scanCtl: scanCtl, - withScanOverview: true, - mimeTypes: []string{"mimeType"}, + assembler := ScanReportAssembler{ + scanChecker: checker, + scanCtl: scanCtl, + overviewOption: model.NewOverviewOptions(model.WithVuln(true)), + mimeTypes: []string{"mimeType"}, } mock.OnAnything(checker, "IsScannable").Return(true, nil) @@ -56,10 +57,10 @@ func (suite *VulAssemblerTestSuite) TestNotScannable() { checker := &scan.Checker{} scanCtl := &scan.Controller{} - assembler := VulAssembler{ - scanChecker: checker, - scanCtl: scanCtl, - withScanOverview: true, + assembler := ScanReportAssembler{ + scanChecker: checker, + scanCtl: scanCtl, + overviewOption: model.NewOverviewOptions(model.WithVuln(true)), } mock.OnAnything(checker, "IsScannable").Return(false, nil) @@ -74,6 +75,32 @@ func (suite *VulAssemblerTestSuite) TestNotScannable() { scanCtl.AssertNotCalled(suite.T(), "GetSummary") } +func (suite *VulAssemblerTestSuite) TestAssembleSBOMOverview() { + checker := &scan.Checker{} + scanCtl := &scan.Controller{} + + assembler := ScanReportAssembler{ + scanChecker: checker, + scanCtl: scanCtl, + overviewOption: model.NewOverviewOptions(model.WithSBOM(true)), + mimeTypes: []string{v1sq.MimeTypeSBOMReport}, + } + + mock.OnAnything(checker, "IsScannable").Return(true, nil) + overview := map[string]interface{}{ + "sbom_digest": "sha256:123456", + "scan_status": "Success", + } + mock.OnAnything(scanCtl, "GetSummary").Return(overview, nil) + + var artifact model.Artifact + err := assembler.WithArtifacts(&artifact).Assemble(context.TODO()) + suite.Nil(err) + suite.Equal(artifact.SBOMOverView["sbom_digest"], "sha256:123456") + suite.Equal(artifact.SBOMOverView["scan_status"], "Success") + +} + func TestVulAssemblerTestSuite(t *testing.T) { suite.Run(t, &VulAssemblerTestSuite{}) } diff --git a/src/server/v2.0/handler/model/artifact.go b/src/server/v2.0/handler/model/artifact.go index 3627d9ced..f931c0abf 100644 --- a/src/server/v2.0/handler/model/artifact.go +++ b/src/server/v2.0/handler/model/artifact.go @@ -28,7 +28,9 @@ import ( // Artifact model type Artifact struct { artifact.Artifact + // TODO: rename to VulOverview ScanOverview map[string]interface{} `json:"scan_overview"` + SBOMOverView map[string]interface{} `json:"sbom_overview"` } // ToSwagger converts the artifact to the swagger model @@ -84,6 +86,18 @@ func (a *Artifact) ToSwagger() *models.Artifact { art.ScanOverview[key] = summary } } + if len(a.SBOMOverView) > 0 { + js, err := json.Marshal(a.SBOMOverView) + if err != nil { + log.Warningf("convert sbom summary failed, error: %v", err) + } + sbomOverview := &models.SBOMOverview{} + err = json.Unmarshal(js, sbomOverview) + if err != nil { + log.Warningf("failed to get sbom summary: error: %v", err) + } + art.SbomOverview = sbomOverview + } return art } diff --git a/src/server/v2.0/handler/model/option.go b/src/server/v2.0/handler/model/option.go new file mode 100644 index 000000000..06aab9943 --- /dev/null +++ b/src/server/v2.0/handler/model/option.go @@ -0,0 +1,47 @@ +// 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 + +// OverviewOptions define the option to query overview info +type OverviewOptions struct { + WithVuln bool + WithSBOM bool +} + +// Option define the func to build options +type Option func(*OverviewOptions) + +// NewOverviewOptions create a new OverviewOptions +func NewOverviewOptions(options ...Option) *OverviewOptions { + opts := &OverviewOptions{} + for _, f := range options { + f(opts) + } + return opts +} + +// WithVuln set the option to query vulnerability info +func WithVuln(enable bool) Option { + return func(o *OverviewOptions) { + o.WithVuln = enable + } +} + +// WithSBOM set the option to query SBOM info +func WithSBOM(enable bool) Option { + return func(o *OverviewOptions) { + o.WithSBOM = enable + } +} diff --git a/src/server/v2.0/handler/model/option_test.go b/src/server/v2.0/handler/model/option_test.go new file mode 100644 index 000000000..1b1bf2f37 --- /dev/null +++ b/src/server/v2.0/handler/model/option_test.go @@ -0,0 +1,33 @@ +// 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 ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestOverviewOptions(t *testing.T) { + // Test NewOverviewOptions with WithVuln and WithSBOM + opts := NewOverviewOptions(WithVuln(true), WithSBOM(true)) + assert.True(t, opts.WithVuln) + assert.True(t, opts.WithSBOM) + + // Test NewOverviewOptions with WithVuln and WithSBOM set to false + opts = NewOverviewOptions(WithVuln(false), WithSBOM(false)) + assert.False(t, opts.WithVuln) + assert.False(t, opts.WithSBOM) +} From 4c9e84cae1a92341829e1cff24b8f66e81cb943a Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Thu, 11 Apr 2024 10:05:05 +0800 Subject: [PATCH 011/100] fix: update e2e test engine images (#20223) Signed-off-by: Shengwen Yu --- tests/test-engine-image/Dockerfile.common | 12 ++++++------ tests/test-engine-image/Dockerfile.ui_test | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test-engine-image/Dockerfile.common b/tests/test-engine-image/Dockerfile.common index 713ad234f..b5151ccf1 100644 --- a/tests/test-engine-image/Dockerfile.common +++ b/tests/test-engine-image/Dockerfile.common @@ -22,15 +22,15 @@ RUN apt-get update && apt-get install -y software-properties-common && \ RUN pwd && mkdir /tool/binary && \ # Install CONTAINERD - CONTAINERD_VERSION=1.7.8 && \ + CONTAINERD_VERSION=1.7.14 && \ wget https://github.com/containerd/containerd/releases/download/v$CONTAINERD_VERSION/containerd-$CONTAINERD_VERSION-linux-amd64.tar.gz && \ tar zxvf containerd-$CONTAINERD_VERSION-linux-amd64.tar.gz && \ cd bin && cp -f containerd ctr /tool/binary/ && \ # docker compose - curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /tool/binary/docker-compose && \ + curl -L "https://github.com/docker/compose/releases/download/v2.26.1/docker-compose-$(uname -s)-$(uname -m)" -o /tool/binary/docker-compose && \ chmod +x /tool/binary/docker-compose && \ # Install helm - HELM_VERSION=3.13.1 && wget https://get.helm.sh/helm-v$HELM_VERSION-linux-amd64.tar.gz && \ + HELM_VERSION=3.14.3 && wget https://get.helm.sh/helm-v$HELM_VERSION-linux-amd64.tar.gz && \ tar zxvf helm-v$HELM_VERSION-linux-amd64.tar.gz && \ ls || pwd && \ mv linux-amd64/helm /tool/binary/helm && \ @@ -54,13 +54,13 @@ RUN pwd && mkdir /tool/binary && \ WASM_TO_OCI_VERSION=0.1.2 && wget https://github.com/engineerd/wasm-to-oci/releases/download/v${WASM_TO_OCI_VERSION}/linux-amd64-wasm-to-oci && \ chmod +x linux-amd64-wasm-to-oci && mv linux-amd64-wasm-to-oci /tool/binary/wasm-to-oci && \ # Install imgpkg - IMGPKG_VERSION=0.39.0 && wget https://github.com/vmware-tanzu/carvel-imgpkg/releases/download/v$IMGPKG_VERSION/imgpkg-linux-amd64 && \ + IMGPKG_VERSION=0.41.1 && wget https://github.com/vmware-tanzu/carvel-imgpkg/releases/download/v$IMGPKG_VERSION/imgpkg-linux-amd64 && \ mv imgpkg-linux-amd64 /tool/binary/imgpkg && chmod +x /tool/binary/imgpkg && \ # Install cosign - COSIGN_VERSION=2.2.0 && wget https://github.com/sigstore/cosign/releases/download/v$COSIGN_VERSION/cosign-linux-amd64 && \ + COSIGN_VERSION=2.2.3 && wget https://github.com/sigstore/cosign/releases/download/v$COSIGN_VERSION/cosign-linux-amd64 && \ mv cosign-linux-amd64 /tool/binary/cosign && chmod +x /tool/binary/cosign && \ # # Install notation - NOTATION_VERSION=1.0.0 && wget https://github.com/notaryproject/notation/releases/download/v$NOTATION_VERSION/notation_${NOTATION_VERSION}_linux_amd64.tar.gz && \ + NOTATION_VERSION=1.1.0 && wget https://github.com/notaryproject/notation/releases/download/v$NOTATION_VERSION/notation_${NOTATION_VERSION}_linux_amd64.tar.gz && \ tar zxvf notation_${NOTATION_VERSION}_linux_amd64.tar.gz && \ mv notation /tool/binary/notation && chmod +x /tool/binary/notation && \ pwd diff --git a/tests/test-engine-image/Dockerfile.ui_test b/tests/test-engine-image/Dockerfile.ui_test index 89f5535e0..9a391089e 100644 --- a/tests/test-engine-image/Dockerfile.ui_test +++ b/tests/test-engine-image/Dockerfile.ui_test @@ -41,8 +41,8 @@ RUN pip3 install --upgrade pip pyasn1 google-apitools==0.5.31 gsutil \ requests dbbot robotframework-seleniumlibrary robotframework-pabot \ robotframework-JSONLibrary hurry.filesize --upgrade && \ apt-get clean all -# Upgrade chromedriver version to 119.0.6045.105 -RUN wget -N https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.6045.105/linux64/chromedriver-linux64.zip && \ +# Upgrade chromedriver version to 123.0.6312.86 +RUN wget -N https://storage.googleapis.com/chrome-for-testing-public/123.0.6312.86/linux64/chromedriver-linux64.zip && \ unzip -j chromedriver-linux64.zip && \ chmod +x chromedriver && \ mv -f chromedriver /usr/local/share/chromedriver && \ @@ -51,7 +51,7 @@ RUN wget -N https://edgedl.me.gvt1.com/edgedl/chrome/chrome-for-testing/119.0.60 RUN pwd && ls && \ # Install docker - DOCKER_VERSION=24.0.2 && wget https://download.docker.com/linux/static/stable/x86_64/docker-$DOCKER_VERSION.tgz && \ + DOCKER_VERSION=26.0.0 && wget https://download.docker.com/linux/static/stable/x86_64/docker-$DOCKER_VERSION.tgz && \ tar --strip-components=1 -xvzf docker-$DOCKER_VERSION.tgz -C /usr/bin && \ rm docker-$DOCKER_VERSION.tgz From 643e84cdfeedf4a0840efb25b080c9c950ef27a0 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Thu, 11 Apr 2024 10:47:07 +0800 Subject: [PATCH 012/100] feat: expose `trivy.timeout` to configure the duration to wait for scan completion (#20257) Signed-off-by: Shengwen Yu --- make/harbor.yml.tmpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/make/harbor.yml.tmpl b/make/harbor.yml.tmpl index 72c9dff44..22e421691 100644 --- a/make/harbor.yml.tmpl +++ b/make/harbor.yml.tmpl @@ -115,6 +115,11 @@ trivy: # # insecure The flag to skip verifying registry certificate insecure: false + # + # timeout The duration to wait for scan completion. + # There is upper bound of 30 minutes defined in scan job. So if this `timeout` is larger than 30m0s, it will also timeout at 30m0s. + timeout: 5m0s + # # github_token The GitHub access token to download Trivy DB # # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough From e9d2f50669a2b4ae7022c4d20d079ba3e6cec957 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Thu, 11 Apr 2024 11:37:59 +0800 Subject: [PATCH 013/100] update mockery to v2.42.2 (#20258) Signed-off-by: yminer --- Makefile | 2 +- .../flow/mock_adapter_factory_test.go | 10 ++- .../replication/flow/mock_adapter_test.go | 70 ++++++++++++++++++- .../replication/mock_flow_controller_test.go | 6 +- src/jobservice/mgt/mock_manager.go | 22 +++++- src/jobservice/period/mock_scheduler.go | 10 ++- src/lib/cache/mock_cache_test.go | 26 ++++++- src/pkg/scheduler/mock_dao_test.go | 30 +++++++- src/pkg/task/mock_execution_dao_test.go | 38 +++++++++- src/pkg/task/mock_jobservice_client_test.go | 22 +++++- src/pkg/task/mock_sweep_manager_test.go | 14 +++- src/pkg/task/mock_task_dao_test.go | 50 ++++++++++++- src/pkg/task/mock_task_manager_test.go | 50 ++++++++++++- src/testing/common/security/context.go | 26 ++++++- src/testing/controller/artifact/controller.go | 50 ++++++++++++- src/testing/controller/blob/controller.go | 70 ++++++++++++++++++- src/testing/controller/config/controller.go | 22 +++++- .../jobservice/scheduler_controller.go | 26 ++++++- src/testing/controller/project/controller.go | 38 +++++++++- .../controller/proxy/remote_interface.go | 18 ++++- src/testing/controller/purge/controller.go | 10 ++- src/testing/controller/quota/controller.go | 42 ++++++++++- .../controller/replication/controller.go | 62 +++++++++++++++- .../controller/repository/controller.go | 34 ++++++++- .../controller/retention/controller.go | 58 ++++++++++++++- src/testing/controller/robot/controller.go | 26 ++++++- src/testing/controller/scan/checker.go | 6 +- src/testing/controller/scan/controller.go | 38 +++++++++- .../controller/scandataexport/controller.go | 22 +++++- src/testing/controller/scanner/controller.go | 50 ++++++++++++- .../controller/securityhub/controller.go | 14 +++- .../controller/systemartifact/controller.go | 6 +- src/testing/controller/task/controller.go | 22 +++++- .../controller/task/execution_controller.go | 22 +++++- src/testing/controller/user/controller.go | 58 ++++++++++++++- src/testing/controller/webhook/controller.go | 58 ++++++++++++++- src/testing/lib/cache/cache.go | 26 ++++++- src/testing/lib/cache/iterator.go | 10 ++- src/testing/lib/config/manager.go | 34 ++++++++- src/testing/lib/orm/creator.go | 6 +- src/testing/pkg/accessory/dao/dao.go | 30 +++++++- src/testing/pkg/accessory/manager.go | 34 ++++++++- src/testing/pkg/accessory/model/accessory.go | 22 +++++- src/testing/pkg/allowlist/dao/dao.go | 10 ++- src/testing/pkg/allowlist/manager.go | 22 +++++- src/testing/pkg/artifact/manager.go | 42 ++++++++++- src/testing/pkg/audit/dao/dao.go | 30 +++++++- src/testing/pkg/audit/manager.go | 30 +++++++- src/testing/pkg/blob/manager.go | 62 +++++++++++++++- .../cached/manifest/redis/cached_manager.go | 34 ++++++++- src/testing/pkg/immutable/dao/dao.go | 30 +++++++- src/testing/pkg/joblog/dao/dao.go | 14 +++- src/testing/pkg/joblog/manager.go | 14 +++- .../jobmonitor/job_service_monitor_client.go | 14 +++- src/testing/pkg/jobmonitor/pool_manager.go | 6 +- src/testing/pkg/jobmonitor/queue_manager.go | 6 +- src/testing/pkg/jobmonitor/redis_client.go | 18 ++++- src/testing/pkg/jobmonitor/worker_manager.go | 6 +- src/testing/pkg/label/dao/dao.go | 42 ++++++++++- src/testing/pkg/label/manager.go | 46 +++++++++++- src/testing/pkg/ldap/manager.go | 18 ++++- src/testing/pkg/member/fake_member_manager.go | 42 ++++++++++- .../pkg/notification/policy/dao/dao.go | 26 ++++++- .../pkg/notification/policy/manager.go | 30 +++++++- src/testing/pkg/oidc/dao/meta_dao.go | 22 +++++- src/testing/pkg/oidc/meta_manager.go | 26 ++++++- src/testing/pkg/project/manager.go | 30 +++++++- src/testing/pkg/project/metadata/manager.go | 22 +++++- src/testing/pkg/queuestatus/manager.go | 22 +++++- src/testing/pkg/quota/driver/driver.go | 22 +++++- src/testing/pkg/quota/manager.go | 30 +++++++- src/testing/pkg/rbac/dao/dao.go | 34 ++++++++- src/testing/pkg/rbac/manager.go | 34 ++++++++- src/testing/pkg/reg/adapter/adapter.go | 14 +++- src/testing/pkg/reg/dao/dao.go | 26 ++++++- src/testing/pkg/reg/manager.go | 38 +++++++++- .../pkg/registry/fake_registry_client.go | 66 ++++++++++++++++- src/testing/pkg/replication/dao/dao.go | 26 ++++++- src/testing/pkg/replication/manager.go | 26 ++++++- src/testing/pkg/repository/dao/dao.go | 34 ++++++++- src/testing/pkg/repository/manager.go | 38 +++++++++- src/testing/pkg/robot/dao/dao.go | 30 +++++++- src/testing/pkg/robot/manager.go | 30 +++++++- .../scan/export/artifact_digest_calculator.go | 6 +- .../pkg/scan/export/filter_processor.go | 14 +++- src/testing/pkg/scan/export/manager.go | 6 +- src/testing/pkg/scan/report/manager.go | 30 +++++++- src/testing/pkg/scan/rest/v1/client.go | 14 +++- src/testing/pkg/scan/rest/v1/client_pool.go | 6 +- .../pkg/scan/rest/v1/request_resolver.go | 2 +- .../pkg/scan/rest/v1/response_handler.go | 6 +- src/testing/pkg/scan/scanner/manager.go | 38 +++++++++- src/testing/pkg/scheduler/scheduler.go | 26 ++++++- src/testing/pkg/securityhub/manager.go | 30 +++++++- .../pkg/systemartifact/cleanup/selector.go | 10 ++- src/testing/pkg/systemartifact/dao/dao.go | 22 +++++- src/testing/pkg/systemartifact/manager.go | 34 ++++++++- src/testing/pkg/task/execution_manager.go | 50 ++++++++++++- src/testing/pkg/task/manager.go | 50 ++++++++++++- src/testing/pkg/user/dao/dao.go | 22 +++++- src/testing/pkg/user/manager.go | 54 +++++++++++++- .../pkg/usergroup/fake_usergroup_manager.go | 34 ++++++++- 102 files changed, 2754 insertions(+), 102 deletions(-) diff --git a/Makefile b/Makefile index 2c10331f0..16a532a52 100644 --- a/Makefile +++ b/Makefile @@ -312,7 +312,7 @@ gen_apis: lint_apis MOCKERY_IMAGENAME=$(IMAGENAMESPACE)/mockery -MOCKERY_VERSION=v2.35.4 +MOCKERY_VERSION=v2.42.2 MOCKERY=$(RUNCONTAINER) ${MOCKERY_IMAGENAME}:${MOCKERY_VERSION} MOCKERY_IMAGE_BUILD_CMD=${DOCKERBUILD} -f ${TOOLSPATH}/mockery/Dockerfile --build-arg GOLANG=${GOBUILDIMAGE} --build-arg MOCKERY_VERSION=${MOCKERY_VERSION} -t ${MOCKERY_IMAGENAME}:$(MOCKERY_VERSION) . diff --git a/src/controller/replication/flow/mock_adapter_factory_test.go b/src/controller/replication/flow/mock_adapter_factory_test.go index e1d069b07..e11e8baca 100644 --- a/src/controller/replication/flow/mock_adapter_factory_test.go +++ b/src/controller/replication/flow/mock_adapter_factory_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package flow @@ -18,6 +18,10 @@ type mockFactory struct { func (_m *mockFactory) AdapterPattern() *model.AdapterPattern { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for AdapterPattern") + } + var r0 *model.AdapterPattern if rf, ok := ret.Get(0).(func() *model.AdapterPattern); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *mockFactory) AdapterPattern() *model.AdapterPattern { func (_m *mockFactory) Create(_a0 *model.Registry) (adapter.Adapter, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 adapter.Adapter var r1 error if rf, ok := ret.Get(0).(func(*model.Registry) (adapter.Adapter, error)); ok { diff --git a/src/controller/replication/flow/mock_adapter_test.go b/src/controller/replication/flow/mock_adapter_test.go index f5fd79158..331b98e2f 100644 --- a/src/controller/replication/flow/mock_adapter_test.go +++ b/src/controller/replication/flow/mock_adapter_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package flow @@ -21,6 +21,10 @@ type mockAdapter struct { func (_m *mockAdapter) BlobExist(repository string, digest string) (bool, error) { ret := _m.Called(repository, digest) + if len(ret) == 0 { + panic("no return value specified for BlobExist") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(string, string) (bool, error)); ok { @@ -45,6 +49,10 @@ func (_m *mockAdapter) BlobExist(repository string, digest string) (bool, error) func (_m *mockAdapter) CanBeMount(digest string) (bool, string, error) { ret := _m.Called(digest) + if len(ret) == 0 { + panic("no return value specified for CanBeMount") + } + var r0 bool var r1 string var r2 error @@ -76,6 +84,10 @@ func (_m *mockAdapter) CanBeMount(digest string) (bool, string, error) { func (_m *mockAdapter) DeleteManifest(repository string, reference string) error { ret := _m.Called(repository, reference) + if len(ret) == 0 { + panic("no return value specified for DeleteManifest") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(repository, reference) @@ -90,6 +102,10 @@ func (_m *mockAdapter) DeleteManifest(repository string, reference string) error func (_m *mockAdapter) DeleteTag(repository string, tag string) error { ret := _m.Called(repository, tag) + if len(ret) == 0 { + panic("no return value specified for DeleteTag") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(repository, tag) @@ -104,6 +120,10 @@ func (_m *mockAdapter) DeleteTag(repository string, tag string) error { func (_m *mockAdapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resource, error) { ret := _m.Called(filters) + if len(ret) == 0 { + panic("no return value specified for FetchArtifacts") + } + var r0 []*model.Resource var r1 error if rf, ok := ret.Get(0).(func([]*model.Filter) ([]*model.Resource, error)); ok { @@ -130,6 +150,10 @@ func (_m *mockAdapter) FetchArtifacts(filters []*model.Filter) ([]*model.Resourc func (_m *mockAdapter) HealthCheck() (string, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthCheck") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func() (string, error)); ok { @@ -154,6 +178,10 @@ func (_m *mockAdapter) HealthCheck() (string, error) { func (_m *mockAdapter) Info() (*model.RegistryInfo, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Info") + } + var r0 *model.RegistryInfo var r1 error if rf, ok := ret.Get(0).(func() (*model.RegistryInfo, error)); ok { @@ -180,6 +208,10 @@ func (_m *mockAdapter) Info() (*model.RegistryInfo, error) { func (_m *mockAdapter) ListTags(repository string) ([]string, error) { ret := _m.Called(repository) + if len(ret) == 0 { + panic("no return value specified for ListTags") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { @@ -206,6 +238,10 @@ func (_m *mockAdapter) ListTags(repository string) ([]string, error) { func (_m *mockAdapter) ManifestExist(repository string, reference string) (bool, *distribution.Descriptor, error) { ret := _m.Called(repository, reference) + if len(ret) == 0 { + panic("no return value specified for ManifestExist") + } + var r0 bool var r1 *distribution.Descriptor var r2 error @@ -239,6 +275,10 @@ func (_m *mockAdapter) ManifestExist(repository string, reference string) (bool, func (_m *mockAdapter) MountBlob(srcRepository string, digest string, dstRepository string) error { ret := _m.Called(srcRepository, digest, dstRepository) + if len(ret) == 0 { + panic("no return value specified for MountBlob") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string, string) error); ok { r0 = rf(srcRepository, digest, dstRepository) @@ -253,6 +293,10 @@ func (_m *mockAdapter) MountBlob(srcRepository string, digest string, dstReposit func (_m *mockAdapter) PrepareForPush(_a0 []*model.Resource) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for PrepareForPush") + } + var r0 error if rf, ok := ret.Get(0).(func([]*model.Resource) error); ok { r0 = rf(_a0) @@ -267,6 +311,10 @@ func (_m *mockAdapter) PrepareForPush(_a0 []*model.Resource) error { func (_m *mockAdapter) PullBlob(repository string, digest string) (int64, io.ReadCloser, error) { ret := _m.Called(repository, digest) + if len(ret) == 0 { + panic("no return value specified for PullBlob") + } + var r0 int64 var r1 io.ReadCloser var r2 error @@ -300,6 +348,10 @@ func (_m *mockAdapter) PullBlob(repository string, digest string) (int64, io.Rea func (_m *mockAdapter) PullBlobChunk(repository string, digest string, blobSize int64, start int64, end int64) (int64, io.ReadCloser, error) { ret := _m.Called(repository, digest, blobSize, start, end) + if len(ret) == 0 { + panic("no return value specified for PullBlobChunk") + } + var r0 int64 var r1 io.ReadCloser var r2 error @@ -340,6 +392,10 @@ func (_m *mockAdapter) PullManifest(repository string, reference string, acceptt _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PullManifest") + } + var r0 distribution.Manifest var r1 string var r2 error @@ -373,6 +429,10 @@ func (_m *mockAdapter) PullManifest(repository string, reference string, acceptt func (_m *mockAdapter) PushBlob(repository string, digest string, size int64, blob io.Reader) error { ret := _m.Called(repository, digest, size, blob) + if len(ret) == 0 { + panic("no return value specified for PushBlob") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader) error); ok { r0 = rf(repository, digest, size, blob) @@ -387,6 +447,10 @@ func (_m *mockAdapter) PushBlob(repository string, digest string, size int64, bl func (_m *mockAdapter) PushBlobChunk(repository string, digest string, size int64, chunk io.Reader, start int64, end int64, location string) (string, int64, error) { ret := _m.Called(repository, digest, size, chunk, start, end, location) + if len(ret) == 0 { + panic("no return value specified for PushBlobChunk") + } + var r0 string var r1 int64 var r2 error @@ -418,6 +482,10 @@ func (_m *mockAdapter) PushBlobChunk(repository string, digest string, size int6 func (_m *mockAdapter) PushManifest(repository string, reference string, mediaType string, payload []byte) (string, error) { ret := _m.Called(repository, reference, mediaType, payload) + if len(ret) == 0 { + panic("no return value specified for PushManifest") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, string, string, []byte) (string, error)); ok { diff --git a/src/controller/replication/mock_flow_controller_test.go b/src/controller/replication/mock_flow_controller_test.go index cd0728a5c..a3d6ec854 100644 --- a/src/controller/replication/mock_flow_controller_test.go +++ b/src/controller/replication/mock_flow_controller_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package replication @@ -21,6 +21,10 @@ type flowController struct { func (_m *flowController) Start(ctx context.Context, executionID int64, policy *model.Policy, resource *regmodel.Resource) error { ret := _m.Called(ctx, executionID, policy, resource) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, *model.Policy, *regmodel.Resource) error); ok { r0 = rf(ctx, executionID, policy, resource) diff --git a/src/jobservice/mgt/mock_manager.go b/src/jobservice/mgt/mock_manager.go index 12a12231e..8f1ccd3a2 100644 --- a/src/jobservice/mgt/mock_manager.go +++ b/src/jobservice/mgt/mock_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package mgt @@ -18,6 +18,10 @@ type MockManager struct { func (_m *MockManager) GetJob(jobID string) (*job.Stats, error) { ret := _m.Called(jobID) + if len(ret) == 0 { + panic("no return value specified for GetJob") + } + var r0 *job.Stats var r1 error if rf, ok := ret.Get(0).(func(string) (*job.Stats, error)); ok { @@ -44,6 +48,10 @@ func (_m *MockManager) GetJob(jobID string) (*job.Stats, error) { func (_m *MockManager) GetJobs(q *query.Parameter) ([]*job.Stats, int64, error) { ret := _m.Called(q) + if len(ret) == 0 { + panic("no return value specified for GetJobs") + } + var r0 []*job.Stats var r1 int64 var r2 error @@ -77,6 +85,10 @@ func (_m *MockManager) GetJobs(q *query.Parameter) ([]*job.Stats, int64, error) func (_m *MockManager) GetPeriodicExecution(pID string, q *query.Parameter) ([]*job.Stats, int64, error) { ret := _m.Called(pID, q) + if len(ret) == 0 { + panic("no return value specified for GetPeriodicExecution") + } + var r0 []*job.Stats var r1 int64 var r2 error @@ -110,6 +122,10 @@ func (_m *MockManager) GetPeriodicExecution(pID string, q *query.Parameter) ([]* func (_m *MockManager) GetScheduledJobs(q *query.Parameter) ([]*job.Stats, int64, error) { ret := _m.Called(q) + if len(ret) == 0 { + panic("no return value specified for GetScheduledJobs") + } + var r0 []*job.Stats var r1 int64 var r2 error @@ -143,6 +159,10 @@ func (_m *MockManager) GetScheduledJobs(q *query.Parameter) ([]*job.Stats, int64 func (_m *MockManager) SaveJob(_a0 *job.Stats) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SaveJob") + } + var r0 error if rf, ok := ret.Get(0).(func(*job.Stats) error); ok { r0 = rf(_a0) diff --git a/src/jobservice/period/mock_scheduler.go b/src/jobservice/period/mock_scheduler.go index 9a8bb611f..abbf494b7 100644 --- a/src/jobservice/period/mock_scheduler.go +++ b/src/jobservice/period/mock_scheduler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package period @@ -13,6 +13,10 @@ type MockScheduler struct { func (_m *MockScheduler) Schedule(policy *Policy) (int64, error) { ret := _m.Called(policy) + if len(ret) == 0 { + panic("no return value specified for Schedule") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(*Policy) (int64, error)); ok { @@ -42,6 +46,10 @@ func (_m *MockScheduler) Start() { func (_m *MockScheduler) UnSchedule(policyID string) error { ret := _m.Called(policyID) + if len(ret) == 0 { + panic("no return value specified for UnSchedule") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(policyID) diff --git a/src/lib/cache/mock_cache_test.go b/src/lib/cache/mock_cache_test.go index 02616848a..62af8f2bb 100644 --- a/src/lib/cache/mock_cache_test.go +++ b/src/lib/cache/mock_cache_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package cache @@ -18,6 +18,10 @@ type mockCache struct { func (_m *mockCache) Contains(ctx context.Context, key string) bool { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Contains") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, key) @@ -32,6 +36,10 @@ func (_m *mockCache) Contains(ctx context.Context, key string) bool { func (_m *mockCache) Delete(ctx context.Context, key string) error { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, key) @@ -46,6 +54,10 @@ func (_m *mockCache) Delete(ctx context.Context, key string) error { func (_m *mockCache) Fetch(ctx context.Context, key string, value interface{}) error { ret := _m.Called(ctx, key, value) + if len(ret) == 0 { + panic("no return value specified for Fetch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { r0 = rf(ctx, key, value) @@ -60,6 +72,10 @@ func (_m *mockCache) Fetch(ctx context.Context, key string, value interface{}) e func (_m *mockCache) Ping(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -81,6 +97,10 @@ func (_m *mockCache) Save(ctx context.Context, key string, value interface{}, ex _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Save") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, interface{}, ...time.Duration) error); ok { r0 = rf(ctx, key, value, expiration...) @@ -95,6 +115,10 @@ func (_m *mockCache) Save(ctx context.Context, key string, value interface{}, ex func (_m *mockCache) Scan(ctx context.Context, match string) (Iterator, error) { ret := _m.Called(ctx, match) + if len(ret) == 0 { + panic("no return value specified for Scan") + } + var r0 Iterator var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (Iterator, error)); ok { diff --git a/src/pkg/scheduler/mock_dao_test.go b/src/pkg/scheduler/mock_dao_test.go index 69783d30b..8fc6ea3c5 100644 --- a/src/pkg/scheduler/mock_dao_test.go +++ b/src/pkg/scheduler/mock_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scheduler @@ -18,6 +18,10 @@ type mockDAO struct { func (_m *mockDAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -42,6 +46,10 @@ func (_m *mockDAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *mockDAO) Create(ctx context.Context, s *schedule) (int64, error) { ret := _m.Called(ctx, s) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *schedule) (int64, error)); ok { @@ -66,6 +74,10 @@ func (_m *mockDAO) Create(ctx context.Context, s *schedule) (int64, error) { func (_m *mockDAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -80,6 +92,10 @@ func (_m *mockDAO) Delete(ctx context.Context, id int64) error { func (_m *mockDAO) Get(ctx context.Context, id int64) (*schedule, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*schedule, error)); ok { @@ -106,6 +122,10 @@ func (_m *mockDAO) Get(ctx context.Context, id int64) (*schedule, error) { func (_m *mockDAO) List(ctx context.Context, query *q.Query) ([]*schedule, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*schedule, error)); ok { @@ -139,6 +159,10 @@ func (_m *mockDAO) Update(ctx context.Context, s *schedule, props ...string) err _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *schedule, ...string) error); ok { r0 = rf(ctx, s, props...) @@ -153,6 +177,10 @@ func (_m *mockDAO) Update(ctx context.Context, s *schedule, props ...string) err func (_m *mockDAO) UpdateRevision(ctx context.Context, id int64, revision int64) (int64, error) { ret := _m.Called(ctx, id, revision) + if len(ret) == 0 { + panic("no return value specified for UpdateRevision") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) (int64, error)); ok { diff --git a/src/pkg/task/mock_execution_dao_test.go b/src/pkg/task/mock_execution_dao_test.go index 0f8051169..f26a035a6 100644 --- a/src/pkg/task/mock_execution_dao_test.go +++ b/src/pkg/task/mock_execution_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -20,6 +20,10 @@ type mockExecutionDAO struct { func (_m *mockExecutionDAO) AsyncRefreshStatus(ctx context.Context, id int64, vendor string) error { ret := _m.Called(ctx, id, vendor) + if len(ret) == 0 { + panic("no return value specified for AsyncRefreshStatus") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, id, vendor) @@ -34,6 +38,10 @@ func (_m *mockExecutionDAO) AsyncRefreshStatus(ctx context.Context, id int64, ve func (_m *mockExecutionDAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -58,6 +66,10 @@ func (_m *mockExecutionDAO) Count(ctx context.Context, query *q.Query) (int64, e func (_m *mockExecutionDAO) Create(ctx context.Context, execution *dao.Execution) (int64, error) { ret := _m.Called(ctx, execution) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Execution) (int64, error)); ok { @@ -82,6 +94,10 @@ func (_m *mockExecutionDAO) Create(ctx context.Context, execution *dao.Execution func (_m *mockExecutionDAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -96,6 +112,10 @@ func (_m *mockExecutionDAO) Delete(ctx context.Context, id int64) error { func (_m *mockExecutionDAO) Get(ctx context.Context, id int64) (*dao.Execution, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *dao.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Execution, error)); ok { @@ -122,6 +142,10 @@ func (_m *mockExecutionDAO) Get(ctx context.Context, id int64) (*dao.Execution, func (_m *mockExecutionDAO) GetMetrics(ctx context.Context, id int64) (*dao.Metrics, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetMetrics") + } + var r0 *dao.Metrics var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Metrics, error)); ok { @@ -148,6 +172,10 @@ func (_m *mockExecutionDAO) GetMetrics(ctx context.Context, id int64) (*dao.Metr func (_m *mockExecutionDAO) List(ctx context.Context, query *q.Query) ([]*dao.Execution, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*dao.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Execution, error)); ok { @@ -174,6 +202,10 @@ func (_m *mockExecutionDAO) List(ctx context.Context, query *q.Query) ([]*dao.Ex func (_m *mockExecutionDAO) RefreshStatus(ctx context.Context, id int64) (bool, string, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for RefreshStatus") + } + var r0 bool var r1 string var r2 error @@ -212,6 +244,10 @@ func (_m *mockExecutionDAO) Update(ctx context.Context, execution *dao.Execution _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Execution, ...string) error); ok { r0 = rf(ctx, execution, props...) diff --git a/src/pkg/task/mock_jobservice_client_test.go b/src/pkg/task/mock_jobservice_client_test.go index 4aee354ea..0d9ecfeca 100644 --- a/src/pkg/task/mock_jobservice_client_test.go +++ b/src/pkg/task/mock_jobservice_client_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -18,6 +18,10 @@ type mockJobserviceClient struct { func (_m *mockJobserviceClient) GetExecutions(uuid string) ([]job.Stats, error) { ret := _m.Called(uuid) + if len(ret) == 0 { + panic("no return value specified for GetExecutions") + } + var r0 []job.Stats var r1 error if rf, ok := ret.Get(0).(func(string) ([]job.Stats, error)); ok { @@ -44,6 +48,10 @@ func (_m *mockJobserviceClient) GetExecutions(uuid string) ([]job.Stats, error) func (_m *mockJobserviceClient) GetJobLog(uuid string) ([]byte, error) { ret := _m.Called(uuid) + if len(ret) == 0 { + panic("no return value specified for GetJobLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(string) ([]byte, error)); ok { @@ -70,6 +78,10 @@ func (_m *mockJobserviceClient) GetJobLog(uuid string) ([]byte, error) { func (_m *mockJobserviceClient) GetJobServiceConfig() (*job.Config, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetJobServiceConfig") + } + var r0 *job.Config var r1 error if rf, ok := ret.Get(0).(func() (*job.Config, error)); ok { @@ -96,6 +108,10 @@ func (_m *mockJobserviceClient) GetJobServiceConfig() (*job.Config, error) { func (_m *mockJobserviceClient) PostAction(uuid string, action string) error { ret := _m.Called(uuid, action) + if len(ret) == 0 { + panic("no return value specified for PostAction") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(uuid, action) @@ -110,6 +126,10 @@ func (_m *mockJobserviceClient) PostAction(uuid string, action string) error { func (_m *mockJobserviceClient) SubmitJob(_a0 *models.JobData) (string, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SubmitJob") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(*models.JobData) (string, error)); ok { diff --git a/src/pkg/task/mock_sweep_manager_test.go b/src/pkg/task/mock_sweep_manager_test.go index 4ca1a88d1..735c34304 100644 --- a/src/pkg/task/mock_sweep_manager_test.go +++ b/src/pkg/task/mock_sweep_manager_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -17,6 +17,10 @@ type mockSweepManager struct { func (_m *mockSweepManager) Clean(ctx context.Context, execID []int64) error { ret := _m.Called(ctx, execID) + if len(ret) == 0 { + panic("no return value specified for Clean") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []int64) error); ok { r0 = rf(ctx, execID) @@ -31,6 +35,10 @@ func (_m *mockSweepManager) Clean(ctx context.Context, execID []int64) error { func (_m *mockSweepManager) FixDanglingStateExecution(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for FixDanglingStateExecution") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -45,6 +53,10 @@ func (_m *mockSweepManager) FixDanglingStateExecution(ctx context.Context) error func (_m *mockSweepManager) ListCandidates(ctx context.Context, vendorType string, retainCnt int64) ([]int64, error) { ret := _m.Called(ctx, vendorType, retainCnt) + if len(ret) == 0 { + panic("no return value specified for ListCandidates") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) ([]int64, error)); ok { diff --git a/src/pkg/task/mock_task_dao_test.go b/src/pkg/task/mock_task_dao_test.go index 49ae17e9f..357353bfa 100644 --- a/src/pkg/task/mock_task_dao_test.go +++ b/src/pkg/task/mock_task_dao_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -22,6 +22,10 @@ type mockTaskDAO struct { func (_m *mockTaskDAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -46,6 +50,10 @@ func (_m *mockTaskDAO) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *mockTaskDAO) Create(ctx context.Context, _a1 *dao.Task) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Task) (int64, error)); ok { @@ -70,6 +78,10 @@ func (_m *mockTaskDAO) Create(ctx context.Context, _a1 *dao.Task) (int64, error) func (_m *mockTaskDAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -84,6 +96,10 @@ func (_m *mockTaskDAO) Delete(ctx context.Context, id int64) error { func (_m *mockTaskDAO) ExecutionIDsByVendorAndStatus(ctx context.Context, vendorType string, status string) ([]int64, error) { ret := _m.Called(ctx, vendorType, status) + if len(ret) == 0 { + panic("no return value specified for ExecutionIDsByVendorAndStatus") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]int64, error)); ok { @@ -110,6 +126,10 @@ func (_m *mockTaskDAO) ExecutionIDsByVendorAndStatus(ctx context.Context, vendor func (_m *mockTaskDAO) Get(ctx context.Context, id int64) (*dao.Task, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *dao.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Task, error)); ok { @@ -136,6 +156,10 @@ func (_m *mockTaskDAO) Get(ctx context.Context, id int64) (*dao.Task, error) { func (_m *mockTaskDAO) GetMaxEndTime(ctx context.Context, executionID int64) (time.Time, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for GetMaxEndTime") + } + var r0 time.Time var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (time.Time, error)); ok { @@ -160,6 +184,10 @@ func (_m *mockTaskDAO) GetMaxEndTime(ctx context.Context, executionID int64) (ti func (_m *mockTaskDAO) List(ctx context.Context, query *q.Query) ([]*dao.Task, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*dao.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Task, error)); ok { @@ -186,6 +214,10 @@ func (_m *mockTaskDAO) List(ctx context.Context, query *q.Query) ([]*dao.Task, e func (_m *mockTaskDAO) ListScanTasksByReportUUID(ctx context.Context, uuid string) ([]*dao.Task, error) { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for ListScanTasksByReportUUID") + } + var r0 []*dao.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]*dao.Task, error)); ok { @@ -212,6 +244,10 @@ func (_m *mockTaskDAO) ListScanTasksByReportUUID(ctx context.Context, uuid strin func (_m *mockTaskDAO) ListStatusCount(ctx context.Context, executionID int64) ([]*dao.StatusCount, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for ListStatusCount") + } + var r0 []*dao.StatusCount var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*dao.StatusCount, error)); ok { @@ -245,6 +281,10 @@ func (_m *mockTaskDAO) Update(ctx context.Context, _a1 *dao.Task, props ...strin _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Task, ...string) error); ok { r0 = rf(ctx, _a1, props...) @@ -259,6 +299,10 @@ func (_m *mockTaskDAO) Update(ctx context.Context, _a1 *dao.Task, props ...strin func (_m *mockTaskDAO) UpdateStatus(ctx context.Context, id int64, status string, statusRevision int64) error { ret := _m.Called(ctx, id, status, statusRevision) + if len(ret) == 0 { + panic("no return value specified for UpdateStatus") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string, int64) error); ok { r0 = rf(ctx, id, status, statusRevision) @@ -273,6 +317,10 @@ func (_m *mockTaskDAO) UpdateStatus(ctx context.Context, id int64, status string func (_m *mockTaskDAO) UpdateStatusInBatch(ctx context.Context, jobIDs []string, status string, batchSize int) error { ret := _m.Called(ctx, jobIDs, status, batchSize) + if len(ret) == 0 { + panic("no return value specified for UpdateStatusInBatch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []string, string, int) error); ok { r0 = rf(ctx, jobIDs, status, batchSize) diff --git a/src/pkg/task/mock_task_manager_test.go b/src/pkg/task/mock_task_manager_test.go index 6e740ab0f..bca9c411a 100644 --- a/src/pkg/task/mock_task_manager_test.go +++ b/src/pkg/task/mock_task_manager_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -18,6 +18,10 @@ type mockTaskManager struct { func (_m *mockTaskManager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -49,6 +53,10 @@ func (_m *mockTaskManager) Create(ctx context.Context, executionID int64, job *J _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *Job, ...map[string]interface{}) (int64, error)); ok { @@ -73,6 +81,10 @@ func (_m *mockTaskManager) Create(ctx context.Context, executionID int64, job *J func (_m *mockTaskManager) ExecutionIDsByVendorAndStatus(ctx context.Context, vendorType string, status string) ([]int64, error) { ret := _m.Called(ctx, vendorType, status) + if len(ret) == 0 { + panic("no return value specified for ExecutionIDsByVendorAndStatus") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]int64, error)); ok { @@ -99,6 +111,10 @@ func (_m *mockTaskManager) ExecutionIDsByVendorAndStatus(ctx context.Context, ve func (_m *mockTaskManager) Get(ctx context.Context, id int64) (*Task, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*Task, error)); ok { @@ -125,6 +141,10 @@ func (_m *mockTaskManager) Get(ctx context.Context, id int64) (*Task, error) { func (_m *mockTaskManager) GetLog(ctx context.Context, id int64) ([]byte, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -151,6 +171,10 @@ func (_m *mockTaskManager) GetLog(ctx context.Context, id int64) ([]byte, error) func (_m *mockTaskManager) GetLogByJobID(ctx context.Context, jobID string) ([]byte, error) { ret := _m.Called(ctx, jobID) + if len(ret) == 0 { + panic("no return value specified for GetLogByJobID") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]byte, error)); ok { @@ -177,6 +201,10 @@ func (_m *mockTaskManager) GetLogByJobID(ctx context.Context, jobID string) ([]b func (_m *mockTaskManager) List(ctx context.Context, query *q.Query) ([]*Task, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*Task, error)); ok { @@ -203,6 +231,10 @@ func (_m *mockTaskManager) List(ctx context.Context, query *q.Query) ([]*Task, e func (_m *mockTaskManager) ListScanTasksByReportUUID(ctx context.Context, uuid string) ([]*Task, error) { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for ListScanTasksByReportUUID") + } + var r0 []*Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]*Task, error)); ok { @@ -229,6 +261,10 @@ func (_m *mockTaskManager) ListScanTasksByReportUUID(ctx context.Context, uuid s func (_m *mockTaskManager) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -250,6 +286,10 @@ func (_m *mockTaskManager) Update(ctx context.Context, task *Task, props ...stri _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *Task, ...string) error); ok { r0 = rf(ctx, task, props...) @@ -264,6 +304,10 @@ func (_m *mockTaskManager) Update(ctx context.Context, task *Task, props ...stri func (_m *mockTaskManager) UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs map[string]interface{}) error { ret := _m.Called(ctx, id, extraAttrs) + if len(ret) == 0 { + panic("no return value specified for UpdateExtraAttrs") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, map[string]interface{}) error); ok { r0 = rf(ctx, id, extraAttrs) @@ -278,6 +322,10 @@ func (_m *mockTaskManager) UpdateExtraAttrs(ctx context.Context, id int64, extra func (_m *mockTaskManager) UpdateStatusInBatch(ctx context.Context, jobIDs []string, status string, batchSize int) error { ret := _m.Called(ctx, jobIDs, status, batchSize) + if len(ret) == 0 { + panic("no return value specified for UpdateStatusInBatch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []string, string, int) error); ok { r0 = rf(ctx, jobIDs, status, batchSize) diff --git a/src/testing/common/security/context.go b/src/testing/common/security/context.go index 59bc1dfe0..9f6c80f6b 100644 --- a/src/testing/common/security/context.go +++ b/src/testing/common/security/context.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package security @@ -19,6 +19,10 @@ type Context struct { func (_m *Context) Can(ctx context.Context, action types.Action, resource types.Resource) bool { ret := _m.Called(ctx, action, resource) + if len(ret) == 0 { + panic("no return value specified for Can") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, types.Action, types.Resource) bool); ok { r0 = rf(ctx, action, resource) @@ -33,6 +37,10 @@ func (_m *Context) Can(ctx context.Context, action types.Action, resource types. func (_m *Context) GetUsername() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetUsername") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -47,6 +55,10 @@ func (_m *Context) GetUsername() string { func (_m *Context) IsAuthenticated() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsAuthenticated") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -61,6 +73,10 @@ func (_m *Context) IsAuthenticated() bool { func (_m *Context) IsSolutionUser() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsSolutionUser") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -75,6 +91,10 @@ func (_m *Context) IsSolutionUser() bool { func (_m *Context) IsSysAdmin() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsSysAdmin") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -89,6 +109,10 @@ func (_m *Context) IsSysAdmin() bool { func (_m *Context) Name() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Name") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/src/testing/controller/artifact/controller.go b/src/testing/controller/artifact/controller.go index 96e23c6ea..7af5d556f 100644 --- a/src/testing/controller/artifact/controller.go +++ b/src/testing/controller/artifact/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package artifact @@ -25,6 +25,10 @@ type Controller struct { func (_m *Controller) AddLabel(ctx context.Context, artifactID int64, labelID int64) error { ret := _m.Called(ctx, artifactID, labelID) + if len(ret) == 0 { + panic("no return value specified for AddLabel") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok { r0 = rf(ctx, artifactID, labelID) @@ -39,6 +43,10 @@ func (_m *Controller) AddLabel(ctx context.Context, artifactID int64, labelID in func (_m *Controller) Copy(ctx context.Context, srcRepo string, reference string, dstRepo string) (int64, error) { ret := _m.Called(ctx, srcRepo, reference, dstRepo) + if len(ret) == 0 { + panic("no return value specified for Copy") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (int64, error)); ok { @@ -63,6 +71,10 @@ func (_m *Controller) Copy(ctx context.Context, srcRepo string, reference string func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -87,6 +99,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -101,6 +117,10 @@ func (_m *Controller) Delete(ctx context.Context, id int64) error { func (_m *Controller) Ensure(ctx context.Context, repository string, digest string, option *artifact.ArtOption) (bool, int64, error) { ret := _m.Called(ctx, repository, digest, option) + if len(ret) == 0 { + panic("no return value specified for Ensure") + } + var r0 bool var r1 int64 var r2 error @@ -132,6 +152,10 @@ func (_m *Controller) Ensure(ctx context.Context, repository string, digest stri func (_m *Controller) Get(ctx context.Context, id int64, option *artifact.Option) (*artifact.Artifact, error) { ret := _m.Called(ctx, id, option) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *artifact.Option) (*artifact.Artifact, error)); ok { @@ -158,6 +182,10 @@ func (_m *Controller) Get(ctx context.Context, id int64, option *artifact.Option func (_m *Controller) GetAddition(ctx context.Context, artifactID int64, additionType string) (*processor.Addition, error) { ret := _m.Called(ctx, artifactID, additionType) + if len(ret) == 0 { + panic("no return value specified for GetAddition") + } + var r0 *processor.Addition var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*processor.Addition, error)); ok { @@ -184,6 +212,10 @@ func (_m *Controller) GetAddition(ctx context.Context, artifactID int64, additio func (_m *Controller) GetByReference(ctx context.Context, repository string, reference string, option *artifact.Option) (*artifact.Artifact, error) { ret := _m.Called(ctx, repository, reference, option) + if len(ret) == 0 { + panic("no return value specified for GetByReference") + } + var r0 *artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, *artifact.Option) (*artifact.Artifact, error)); ok { @@ -210,6 +242,10 @@ func (_m *Controller) GetByReference(ctx context.Context, repository string, ref func (_m *Controller) List(ctx context.Context, query *q.Query, option *artifact.Option) ([]*artifact.Artifact, error) { ret := _m.Called(ctx, query, option) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, *artifact.Option) ([]*artifact.Artifact, error)); ok { @@ -236,6 +272,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, option *artifact func (_m *Controller) RemoveLabel(ctx context.Context, artifactID int64, labelID int64) error { ret := _m.Called(ctx, artifactID, labelID) + if len(ret) == 0 { + panic("no return value specified for RemoveLabel") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok { r0 = rf(ctx, artifactID, labelID) @@ -250,6 +290,10 @@ func (_m *Controller) RemoveLabel(ctx context.Context, artifactID int64, labelID func (_m *Controller) UpdatePullTime(ctx context.Context, artifactID int64, tagID int64, _a3 time.Time) error { ret := _m.Called(ctx, artifactID, tagID, _a3) + if len(ret) == 0 { + panic("no return value specified for UpdatePullTime") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64, time.Time) error); ok { r0 = rf(ctx, artifactID, tagID, _a3) @@ -264,6 +308,10 @@ func (_m *Controller) UpdatePullTime(ctx context.Context, artifactID int64, tagI func (_m *Controller) Walk(ctx context.Context, root *artifact.Artifact, walkFn func(*artifact.Artifact) error, option *artifact.Option) error { ret := _m.Called(ctx, root, walkFn, option) + if len(ret) == 0 { + panic("no return value specified for Walk") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, func(*artifact.Artifact) error, *artifact.Option) error); ok { r0 = rf(ctx, root, walkFn, option) diff --git a/src/testing/controller/blob/controller.go b/src/testing/controller/blob/controller.go index fa212af25..3081f5e25 100644 --- a/src/testing/controller/blob/controller.go +++ b/src/testing/controller/blob/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package blob @@ -25,6 +25,10 @@ type Controller struct { func (_m *Controller) AssociateWithArtifact(ctx context.Context, blobDigests []string, artifactDigest string) error { ret := _m.Called(ctx, blobDigests, artifactDigest) + if len(ret) == 0 { + panic("no return value specified for AssociateWithArtifact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []string, string) error); ok { r0 = rf(ctx, blobDigests, artifactDigest) @@ -39,6 +43,10 @@ func (_m *Controller) AssociateWithArtifact(ctx context.Context, blobDigests []s func (_m *Controller) AssociateWithProjectByDigest(ctx context.Context, blobDigest string, projectID int64) error { ret := _m.Called(ctx, blobDigest, projectID) + if len(ret) == 0 { + panic("no return value specified for AssociateWithProjectByDigest") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, blobDigest, projectID) @@ -53,6 +61,10 @@ func (_m *Controller) AssociateWithProjectByDigest(ctx context.Context, blobDige func (_m *Controller) AssociateWithProjectByID(ctx context.Context, blobID int64, projectID int64) error { ret := _m.Called(ctx, blobID, projectID) + if len(ret) == 0 { + panic("no return value specified for AssociateWithProjectByID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok { r0 = rf(ctx, blobID, projectID) @@ -67,6 +79,10 @@ func (_m *Controller) AssociateWithProjectByID(ctx context.Context, blobID int64 func (_m *Controller) CalculateTotalSize(ctx context.Context, excludeForeign bool) (int64, error) { ret := _m.Called(ctx, excludeForeign) + if len(ret) == 0 { + panic("no return value specified for CalculateTotalSize") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, bool) (int64, error)); ok { @@ -91,6 +107,10 @@ func (_m *Controller) CalculateTotalSize(ctx context.Context, excludeForeign boo func (_m *Controller) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeign bool) (int64, error) { ret := _m.Called(ctx, projectID, excludeForeign) + if len(ret) == 0 { + panic("no return value specified for CalculateTotalSizeByProject") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, bool) (int64, error)); ok { @@ -115,6 +135,10 @@ func (_m *Controller) CalculateTotalSizeByProject(ctx context.Context, projectID func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -129,6 +153,10 @@ func (_m *Controller) Delete(ctx context.Context, id int64) error { func (_m *Controller) Ensure(ctx context.Context, digest string, contentType string, size int64) (int64, error) { ret := _m.Called(ctx, digest, contentType, size) + if len(ret) == 0 { + panic("no return value specified for Ensure") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (int64, error)); ok { @@ -160,6 +188,10 @@ func (_m *Controller) Exist(ctx context.Context, digest string, options ...blob. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Exist") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, ...blob.Option) (bool, error)); ok { @@ -184,6 +216,10 @@ func (_m *Controller) Exist(ctx context.Context, digest string, options ...blob. func (_m *Controller) Fail(ctx context.Context, _a1 *models.Blob) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Fail") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) error); ok { r0 = rf(ctx, _a1) @@ -198,6 +234,10 @@ func (_m *Controller) Fail(ctx context.Context, _a1 *models.Blob) error { func (_m *Controller) FindMissingAssociationsForProject(ctx context.Context, projectID int64, blobs []*models.Blob) ([]*models.Blob, error) { ret := _m.Called(ctx, projectID, blobs) + if len(ret) == 0 { + panic("no return value specified for FindMissingAssociationsForProject") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, []*models.Blob) ([]*models.Blob, error)); ok { @@ -231,6 +271,10 @@ func (_m *Controller) Get(ctx context.Context, digest string, options ...blob.Op _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, ...blob.Option) (*models.Blob, error)); ok { @@ -257,6 +301,10 @@ func (_m *Controller) Get(ctx context.Context, digest string, options ...blob.Op func (_m *Controller) GetAcceptedBlobSize(ctx context.Context, sessionID string) (int64, error) { ret := _m.Called(ctx, sessionID) + if len(ret) == 0 { + panic("no return value specified for GetAcceptedBlobSize") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (int64, error)); ok { @@ -281,6 +329,10 @@ func (_m *Controller) GetAcceptedBlobSize(ctx context.Context, sessionID string) func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*models.Blob, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.Blob, error)); ok { @@ -307,6 +359,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*models.Blob, func (_m *Controller) SetAcceptedBlobSize(ctx context.Context, sessionID string, size int64) error { ret := _m.Called(ctx, sessionID, size) + if len(ret) == 0 { + panic("no return value specified for SetAcceptedBlobSize") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, sessionID, size) @@ -321,6 +377,10 @@ func (_m *Controller) SetAcceptedBlobSize(ctx context.Context, sessionID string, func (_m *Controller) Sync(ctx context.Context, references []distribution.Descriptor) error { ret := _m.Called(ctx, references) + if len(ret) == 0 { + panic("no return value specified for Sync") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []distribution.Descriptor) error); ok { r0 = rf(ctx, references) @@ -335,6 +395,10 @@ func (_m *Controller) Sync(ctx context.Context, references []distribution.Descri func (_m *Controller) Touch(ctx context.Context, _a1 *models.Blob) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Touch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) error); ok { r0 = rf(ctx, _a1) @@ -349,6 +413,10 @@ func (_m *Controller) Touch(ctx context.Context, _a1 *models.Blob) error { func (_m *Controller) Update(ctx context.Context, _a1 *models.Blob) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/controller/config/controller.go b/src/testing/controller/config/controller.go index 8fd145413..c82468518 100644 --- a/src/testing/controller/config/controller.go +++ b/src/testing/controller/config/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package config @@ -18,6 +18,10 @@ type Controller struct { func (_m *Controller) AllConfigs(ctx context.Context) (map[string]interface{}, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for AllConfigs") + } + var r0 map[string]interface{} var r1 error if rf, ok := ret.Get(0).(func(context.Context) (map[string]interface{}, error)); ok { @@ -44,6 +48,10 @@ func (_m *Controller) AllConfigs(ctx context.Context) (map[string]interface{}, e func (_m *Controller) ConvertForGet(ctx context.Context, cfg map[string]interface{}, internal bool) (map[string]*models.Value, error) { ret := _m.Called(ctx, cfg, internal) + if len(ret) == 0 { + panic("no return value specified for ConvertForGet") + } + var r0 map[string]*models.Value var r1 error if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}, bool) (map[string]*models.Value, error)); ok { @@ -70,6 +78,10 @@ func (_m *Controller) ConvertForGet(ctx context.Context, cfg map[string]interfac func (_m *Controller) OverwriteConfig(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for OverwriteConfig") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -84,6 +96,10 @@ func (_m *Controller) OverwriteConfig(ctx context.Context) error { func (_m *Controller) UpdateUserConfigs(ctx context.Context, conf map[string]interface{}) error { ret := _m.Called(ctx, conf) + if len(ret) == 0 { + panic("no return value specified for UpdateUserConfigs") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) error); ok { r0 = rf(ctx, conf) @@ -98,6 +114,10 @@ func (_m *Controller) UpdateUserConfigs(ctx context.Context, conf map[string]int func (_m *Controller) UserConfigs(ctx context.Context) (map[string]*models.Value, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for UserConfigs") + } + var r0 map[string]*models.Value var r1 error if rf, ok := ret.Get(0).(func(context.Context) (map[string]*models.Value, error)); ok { diff --git a/src/testing/controller/jobservice/scheduler_controller.go b/src/testing/controller/jobservice/scheduler_controller.go index 806214fd2..4f5a09328 100644 --- a/src/testing/controller/jobservice/scheduler_controller.go +++ b/src/testing/controller/jobservice/scheduler_controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobservice @@ -21,6 +21,10 @@ type SchedulerController struct { func (_m *SchedulerController) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *SchedulerController) Count(ctx context.Context, query *q.Query) (int64 func (_m *SchedulerController) Create(ctx context.Context, vendorType string, cronType string, cron string, callbackFuncName string, policy interface{}, extrasParam map[string]interface{}) (int64, error) { ret := _m.Called(ctx, vendorType, cronType, cron, callbackFuncName, policy, extrasParam) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, interface{}, map[string]interface{}) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *SchedulerController) Create(ctx context.Context, vendorType string, cr func (_m *SchedulerController) Delete(ctx context.Context, vendorType string) error { ret := _m.Called(ctx, vendorType) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, vendorType) @@ -83,6 +95,10 @@ func (_m *SchedulerController) Delete(ctx context.Context, vendorType string) er func (_m *SchedulerController) Get(ctx context.Context, vendorType string) (*scheduler.Schedule, error) { ret := _m.Called(ctx, vendorType) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *scheduler.Schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*scheduler.Schedule, error)); ok { @@ -109,6 +125,10 @@ func (_m *SchedulerController) Get(ctx context.Context, vendorType string) (*sch func (_m *SchedulerController) List(ctx context.Context, query *q.Query) ([]*scheduler.Schedule, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*scheduler.Schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*scheduler.Schedule, error)); ok { @@ -135,6 +155,10 @@ func (_m *SchedulerController) List(ctx context.Context, query *q.Query) ([]*sch func (_m *SchedulerController) Paused(ctx context.Context) (bool, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Paused") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context) (bool, error)); ok { diff --git a/src/testing/controller/project/controller.go b/src/testing/controller/project/controller.go index 69f3563e6..bcb9331b2 100644 --- a/src/testing/controller/project/controller.go +++ b/src/testing/controller/project/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package project @@ -25,6 +25,10 @@ type Controller struct { func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -49,6 +53,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Create(ctx context.Context, _a1 *models.Project) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.Project) (int64, error)); ok { @@ -73,6 +81,10 @@ func (_m *Controller) Create(ctx context.Context, _a1 *models.Project) (int64, e func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -87,6 +99,10 @@ func (_m *Controller) Delete(ctx context.Context, id int64) error { func (_m *Controller) Exists(ctx context.Context, projectIDOrName interface{}) (bool, error) { ret := _m.Called(ctx, projectIDOrName) + if len(ret) == 0 { + panic("no return value specified for Exists") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, interface{}) (bool, error)); ok { @@ -118,6 +134,10 @@ func (_m *Controller) Get(ctx context.Context, projectIDOrName interface{}, opti _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Project var r1 error if rf, ok := ret.Get(0).(func(context.Context, interface{}, ...project.Option) (*models.Project, error)); ok { @@ -151,6 +171,10 @@ func (_m *Controller) GetByName(ctx context.Context, projectName string, options _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetByName") + } + var r0 *models.Project var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, ...project.Option) (*models.Project, error)); ok { @@ -184,6 +208,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...proje _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Project var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, ...project.Option) ([]*models.Project, error)); ok { @@ -210,6 +238,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...proje func (_m *Controller) ListRoles(ctx context.Context, projectID int64, u *commonmodels.User) ([]int, error) { ret := _m.Called(ctx, projectID, u) + if len(ret) == 0 { + panic("no return value specified for ListRoles") + } + var r0 []int var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *commonmodels.User) ([]int, error)); ok { @@ -236,6 +268,10 @@ func (_m *Controller) ListRoles(ctx context.Context, projectID int64, u *commonm func (_m *Controller) Update(ctx context.Context, _a1 *models.Project) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Project) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/controller/proxy/remote_interface.go b/src/testing/controller/proxy/remote_interface.go index eb9361280..48b52a094 100644 --- a/src/testing/controller/proxy/remote_interface.go +++ b/src/testing/controller/proxy/remote_interface.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package proxy @@ -19,6 +19,10 @@ type RemoteInterface struct { func (_m *RemoteInterface) BlobReader(repo string, dig string) (int64, io.ReadCloser, error) { ret := _m.Called(repo, dig) + if len(ret) == 0 { + panic("no return value specified for BlobReader") + } + var r0 int64 var r1 io.ReadCloser var r2 error @@ -52,6 +56,10 @@ func (_m *RemoteInterface) BlobReader(repo string, dig string) (int64, io.ReadCl func (_m *RemoteInterface) ListTags(repo string) ([]string, error) { ret := _m.Called(repo) + if len(ret) == 0 { + panic("no return value specified for ListTags") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { @@ -78,6 +86,10 @@ func (_m *RemoteInterface) ListTags(repo string) ([]string, error) { func (_m *RemoteInterface) Manifest(repo string, ref string) (distribution.Manifest, string, error) { ret := _m.Called(repo, ref) + if len(ret) == 0 { + panic("no return value specified for Manifest") + } + var r0 distribution.Manifest var r1 string var r2 error @@ -111,6 +123,10 @@ func (_m *RemoteInterface) Manifest(repo string, ref string) (distribution.Manif func (_m *RemoteInterface) ManifestExist(repo string, ref string) (bool, *distribution.Descriptor, error) { ret := _m.Called(repo, ref) + if len(ret) == 0 { + panic("no return value specified for ManifestExist") + } + var r0 bool var r1 *distribution.Descriptor var r2 error diff --git a/src/testing/controller/purge/controller.go b/src/testing/controller/purge/controller.go index 3771e28e8..7c3e27fed 100644 --- a/src/testing/controller/purge/controller.go +++ b/src/testing/controller/purge/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package purge @@ -18,6 +18,10 @@ type Controller struct { func (_m *Controller) Start(ctx context.Context, policy purge.JobPolicy, trigger string) (int64, error) { ret := _m.Called(ctx, policy, trigger) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, purge.JobPolicy, string) (int64, error)); ok { @@ -42,6 +46,10 @@ func (_m *Controller) Start(ctx context.Context, policy purge.JobPolicy, trigger func (_m *Controller) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) diff --git a/src/testing/controller/quota/controller.go b/src/testing/controller/quota/controller.go index 23b10a091..7053fb4d7 100644 --- a/src/testing/controller/quota/controller.go +++ b/src/testing/controller/quota/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package quota @@ -24,6 +24,10 @@ type Controller struct { func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -55,6 +59,10 @@ func (_m *Controller) Create(ctx context.Context, reference string, referenceID _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, types.ResourceList, ...types.ResourceList) (int64, error)); ok { @@ -79,6 +87,10 @@ func (_m *Controller) Create(ctx context.Context, reference string, referenceID func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -100,6 +112,10 @@ func (_m *Controller) Get(ctx context.Context, id int64, options ...quota.Option _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, ...quota.Option) (*models.Quota, error)); ok { @@ -133,6 +149,10 @@ func (_m *Controller) GetByRef(ctx context.Context, reference string, referenceI _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetByRef") + } + var r0 *models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, ...quota.Option) (*models.Quota, error)); ok { @@ -159,6 +179,10 @@ func (_m *Controller) GetByRef(ctx context.Context, reference string, referenceI func (_m *Controller) IsEnabled(ctx context.Context, reference string, referenceID string) (bool, error) { ret := _m.Called(ctx, reference, referenceID) + if len(ret) == 0 { + panic("no return value specified for IsEnabled") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { @@ -190,6 +214,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...quota _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, ...quota.Option) ([]*models.Quota, error)); ok { @@ -223,6 +251,10 @@ func (_m *Controller) Refresh(ctx context.Context, reference string, referenceID _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Refresh") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, ...quota.Option) error); ok { r0 = rf(ctx, reference, referenceID, options...) @@ -237,6 +269,10 @@ func (_m *Controller) Refresh(ctx context.Context, reference string, referenceID func (_m *Controller) Request(ctx context.Context, reference string, referenceID string, resources types.ResourceList, f func() error) error { ret := _m.Called(ctx, reference, referenceID, resources, f) + if len(ret) == 0 { + panic("no return value specified for Request") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, types.ResourceList, func() error) error); ok { r0 = rf(ctx, reference, referenceID, resources, f) @@ -251,6 +287,10 @@ func (_m *Controller) Request(ctx context.Context, reference string, referenceID func (_m *Controller) Update(ctx context.Context, _a1 *models.Quota) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Quota) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/controller/replication/controller.go b/src/testing/controller/replication/controller.go index 76138da96..02a2afbb5 100644 --- a/src/testing/controller/replication/controller.go +++ b/src/testing/controller/replication/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package replication @@ -24,6 +24,10 @@ type Controller struct { func (_m *Controller) CreatePolicy(ctx context.Context, policy *model.Policy) (int64, error) { ret := _m.Called(ctx, policy) + if len(ret) == 0 { + panic("no return value specified for CreatePolicy") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -48,6 +52,10 @@ func (_m *Controller) CreatePolicy(ctx context.Context, policy *model.Policy) (i func (_m *Controller) DeletePolicy(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeletePolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -62,6 +70,10 @@ func (_m *Controller) DeletePolicy(ctx context.Context, id int64) error { func (_m *Controller) ExecutionCount(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ExecutionCount") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -86,6 +98,10 @@ func (_m *Controller) ExecutionCount(ctx context.Context, query *q.Query) (int64 func (_m *Controller) GetExecution(ctx context.Context, executionID int64) (*replication.Execution, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for GetExecution") + } + var r0 *replication.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*replication.Execution, error)); ok { @@ -112,6 +128,10 @@ func (_m *Controller) GetExecution(ctx context.Context, executionID int64) (*rep func (_m *Controller) GetPolicy(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetPolicy") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -138,6 +158,10 @@ func (_m *Controller) GetPolicy(ctx context.Context, id int64) (*model.Policy, e func (_m *Controller) GetTask(ctx context.Context, taskID int64) (*replication.Task, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetTask") + } + var r0 *replication.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*replication.Task, error)); ok { @@ -164,6 +188,10 @@ func (_m *Controller) GetTask(ctx context.Context, taskID int64) (*replication.T func (_m *Controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetTaskLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -190,6 +218,10 @@ func (_m *Controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, err func (_m *Controller) ListExecutions(ctx context.Context, query *q.Query) ([]*replication.Execution, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListExecutions") + } + var r0 []*replication.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*replication.Execution, error)); ok { @@ -216,6 +248,10 @@ func (_m *Controller) ListExecutions(ctx context.Context, query *q.Query) ([]*re func (_m *Controller) ListPolicies(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListPolicies") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -242,6 +278,10 @@ func (_m *Controller) ListPolicies(ctx context.Context, query *q.Query) ([]*mode func (_m *Controller) ListTasks(ctx context.Context, query *q.Query) ([]*replication.Task, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListTasks") + } + var r0 []*replication.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*replication.Task, error)); ok { @@ -268,6 +308,10 @@ func (_m *Controller) ListTasks(ctx context.Context, query *q.Query) ([]*replica func (_m *Controller) PolicyCount(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for PolicyCount") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -292,6 +336,10 @@ func (_m *Controller) PolicyCount(ctx context.Context, query *q.Query) (int64, e func (_m *Controller) Start(ctx context.Context, policy *model.Policy, resource *regmodel.Resource, trigger string) (int64, error) { ret := _m.Called(ctx, policy, resource, trigger) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy, *regmodel.Resource, string) (int64, error)); ok { @@ -316,6 +364,10 @@ func (_m *Controller) Start(ctx context.Context, policy *model.Policy, resource func (_m *Controller) Stop(ctx context.Context, executionID int64) error { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, executionID) @@ -330,6 +382,10 @@ func (_m *Controller) Stop(ctx context.Context, executionID int64) error { func (_m *Controller) TaskCount(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for TaskCount") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -361,6 +417,10 @@ func (_m *Controller) UpdatePolicy(ctx context.Context, policy *model.Policy, pr _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdatePolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy, ...string) error); ok { r0 = rf(ctx, policy, props...) diff --git a/src/testing/controller/repository/controller.go b/src/testing/controller/repository/controller.go index 92896a00c..bdc109770 100644 --- a/src/testing/controller/repository/controller.go +++ b/src/testing/controller/repository/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package repository @@ -20,6 +20,10 @@ type Controller struct { func (_m *Controller) AddPullCount(ctx context.Context, id int64, count uint64) error { ret := _m.Called(ctx, id, count) + if len(ret) == 0 { + panic("no return value specified for AddPullCount") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, uint64) error); ok { r0 = rf(ctx, id, count) @@ -34,6 +38,10 @@ func (_m *Controller) AddPullCount(ctx context.Context, id int64, count uint64) func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -58,6 +66,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -72,6 +84,10 @@ func (_m *Controller) Delete(ctx context.Context, id int64) error { func (_m *Controller) Ensure(ctx context.Context, name string) (bool, int64, error) { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for Ensure") + } + var r0 bool var r1 int64 var r2 error @@ -103,6 +119,10 @@ func (_m *Controller) Ensure(ctx context.Context, name string) (bool, int64, err func (_m *Controller) Get(ctx context.Context, id int64) (*model.RepoRecord, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.RepoRecord, error)); ok { @@ -129,6 +149,10 @@ func (_m *Controller) Get(ctx context.Context, id int64) (*model.RepoRecord, err func (_m *Controller) GetByName(ctx context.Context, name string) (*model.RepoRecord, error) { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for GetByName") + } + var r0 *model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*model.RepoRecord, error)); ok { @@ -155,6 +179,10 @@ func (_m *Controller) GetByName(ctx context.Context, name string) (*model.RepoRe func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*model.RepoRecord, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.RepoRecord, error)); ok { @@ -188,6 +216,10 @@ func (_m *Controller) Update(ctx context.Context, _a1 *model.RepoRecord, propert _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.RepoRecord, ...string) error); ok { r0 = rf(ctx, _a1, properties...) diff --git a/src/testing/controller/retention/controller.go b/src/testing/controller/retention/controller.go index d27ce8d0a..8eb3c9889 100644 --- a/src/testing/controller/retention/controller.go +++ b/src/testing/controller/retention/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package retention @@ -22,6 +22,10 @@ type Controller struct { func (_m *Controller) CreateRetention(ctx context.Context, p *policy.Metadata) (int64, error) { ret := _m.Called(ctx, p) + if len(ret) == 0 { + panic("no return value specified for CreateRetention") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *policy.Metadata) (int64, error)); ok { @@ -46,6 +50,10 @@ func (_m *Controller) CreateRetention(ctx context.Context, p *policy.Metadata) ( func (_m *Controller) DeleteRetention(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteRetention") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -60,6 +68,10 @@ func (_m *Controller) DeleteRetention(ctx context.Context, id int64) error { func (_m *Controller) DeleteRetentionByProject(ctx context.Context, projectID int64) error { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for DeleteRetentionByProject") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, projectID) @@ -74,6 +86,10 @@ func (_m *Controller) DeleteRetentionByProject(ctx context.Context, projectID in func (_m *Controller) GetRetention(ctx context.Context, id int64) (*policy.Metadata, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetRetention") + } + var r0 *policy.Metadata var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*policy.Metadata, error)); ok { @@ -100,6 +116,10 @@ func (_m *Controller) GetRetention(ctx context.Context, id int64) (*policy.Metad func (_m *Controller) GetRetentionExec(ctx context.Context, eid int64) (*pkgretention.Execution, error) { ret := _m.Called(ctx, eid) + if len(ret) == 0 { + panic("no return value specified for GetRetentionExec") + } + var r0 *pkgretention.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*pkgretention.Execution, error)); ok { @@ -126,6 +146,10 @@ func (_m *Controller) GetRetentionExec(ctx context.Context, eid int64) (*pkgrete func (_m *Controller) GetRetentionExecTask(ctx context.Context, taskID int64) (*pkgretention.Task, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetRetentionExecTask") + } + var r0 *pkgretention.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*pkgretention.Task, error)); ok { @@ -152,6 +176,10 @@ func (_m *Controller) GetRetentionExecTask(ctx context.Context, taskID int64) (* func (_m *Controller) GetRetentionExecTaskLog(ctx context.Context, taskID int64) ([]byte, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetRetentionExecTaskLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -178,6 +206,10 @@ func (_m *Controller) GetRetentionExecTaskLog(ctx context.Context, taskID int64) func (_m *Controller) GetTotalOfRetentionExecTasks(ctx context.Context, executionID int64) (int64, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for GetTotalOfRetentionExecTasks") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok { @@ -202,6 +234,10 @@ func (_m *Controller) GetTotalOfRetentionExecTasks(ctx context.Context, executio func (_m *Controller) GetTotalOfRetentionExecs(ctx context.Context, policyID int64) (int64, error) { ret := _m.Called(ctx, policyID) + if len(ret) == 0 { + panic("no return value specified for GetTotalOfRetentionExecs") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok { @@ -226,6 +262,10 @@ func (_m *Controller) GetTotalOfRetentionExecs(ctx context.Context, policyID int func (_m *Controller) ListRetentionExecTasks(ctx context.Context, executionID int64, query *q.Query) ([]*pkgretention.Task, error) { ret := _m.Called(ctx, executionID, query) + if len(ret) == 0 { + panic("no return value specified for ListRetentionExecTasks") + } + var r0 []*pkgretention.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) ([]*pkgretention.Task, error)); ok { @@ -252,6 +292,10 @@ func (_m *Controller) ListRetentionExecTasks(ctx context.Context, executionID in func (_m *Controller) ListRetentionExecs(ctx context.Context, policyID int64, query *q.Query) ([]*pkgretention.Execution, error) { ret := _m.Called(ctx, policyID, query) + if len(ret) == 0 { + panic("no return value specified for ListRetentionExecs") + } + var r0 []*pkgretention.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) ([]*pkgretention.Execution, error)); ok { @@ -278,6 +322,10 @@ func (_m *Controller) ListRetentionExecs(ctx context.Context, policyID int64, qu func (_m *Controller) OperateRetentionExec(ctx context.Context, eid int64, action string) error { ret := _m.Called(ctx, eid, action) + if len(ret) == 0 { + panic("no return value specified for OperateRetentionExec") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, eid, action) @@ -292,6 +340,10 @@ func (_m *Controller) OperateRetentionExec(ctx context.Context, eid int64, actio func (_m *Controller) TriggerRetentionExec(ctx context.Context, policyID int64, trigger string, dryRun bool) (int64, error) { ret := _m.Called(ctx, policyID, trigger, dryRun) + if len(ret) == 0 { + panic("no return value specified for TriggerRetentionExec") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, string, bool) (int64, error)); ok { @@ -316,6 +368,10 @@ func (_m *Controller) TriggerRetentionExec(ctx context.Context, policyID int64, func (_m *Controller) UpdateRetention(ctx context.Context, p *policy.Metadata) error { ret := _m.Called(ctx, p) + if len(ret) == 0 { + panic("no return value specified for UpdateRetention") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *policy.Metadata) error); ok { r0 = rf(ctx, p) diff --git a/src/testing/controller/robot/controller.go b/src/testing/controller/robot/controller.go index ab54afe59..03c6af483 100644 --- a/src/testing/controller/robot/controller.go +++ b/src/testing/controller/robot/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package robot @@ -20,6 +20,10 @@ type Controller struct { func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Create(ctx context.Context, r *robot.Robot) (int64, string, error) { ret := _m.Called(ctx, r) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 string var r2 error @@ -75,6 +83,10 @@ func (_m *Controller) Create(ctx context.Context, r *robot.Robot) (int64, string func (_m *Controller) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -89,6 +101,10 @@ func (_m *Controller) Delete(ctx context.Context, id int64) error { func (_m *Controller) Get(ctx context.Context, id int64, option *robot.Option) (*robot.Robot, error) { ret := _m.Called(ctx, id, option) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *robot.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *robot.Option) (*robot.Robot, error)); ok { @@ -115,6 +131,10 @@ func (_m *Controller) Get(ctx context.Context, id int64, option *robot.Option) ( func (_m *Controller) List(ctx context.Context, query *q.Query, option *robot.Option) ([]*robot.Robot, error) { ret := _m.Called(ctx, query, option) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*robot.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, *robot.Option) ([]*robot.Robot, error)); ok { @@ -141,6 +161,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, option *robot.Op func (_m *Controller) Update(ctx context.Context, r *robot.Robot, option *robot.Option) error { ret := _m.Called(ctx, r, option) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *robot.Robot, *robot.Option) error); ok { r0 = rf(ctx, r, option) diff --git a/src/testing/controller/scan/checker.go b/src/testing/controller/scan/checker.go index 71f160185..9c2e4b674 100644 --- a/src/testing/controller/scan/checker.go +++ b/src/testing/controller/scan/checker.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scan @@ -19,6 +19,10 @@ type Checker struct { func (_m *Checker) IsScannable(ctx context.Context, _a1 *artifact.Artifact) (bool, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for IsScannable") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact) (bool, error)); ok { diff --git a/src/testing/controller/scan/controller.go b/src/testing/controller/scan/controller.go index bd8001380..5ec8091a4 100644 --- a/src/testing/controller/scan/controller.go +++ b/src/testing/controller/scan/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scan @@ -32,6 +32,10 @@ func (_m *Controller) DeleteReports(ctx context.Context, digests ...string) erro _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteReports") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, ...string) error); ok { r0 = rf(ctx, digests...) @@ -46,6 +50,10 @@ func (_m *Controller) DeleteReports(ctx context.Context, digests ...string) erro func (_m *Controller) GetReport(ctx context.Context, _a1 *artifact.Artifact, mimeTypes []string) ([]*daoscan.Report, error) { ret := _m.Called(ctx, _a1, mimeTypes) + if len(ret) == 0 { + panic("no return value specified for GetReport") + } + var r0 []*daoscan.Report var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []string) ([]*daoscan.Report, error)); ok { @@ -72,6 +80,10 @@ func (_m *Controller) GetReport(ctx context.Context, _a1 *artifact.Artifact, mim func (_m *Controller) GetScanLog(ctx context.Context, art *artifact.Artifact, uuid string) ([]byte, error) { ret := _m.Called(ctx, art, uuid) + if len(ret) == 0 { + panic("no return value specified for GetScanLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, string) ([]byte, error)); ok { @@ -98,6 +110,10 @@ func (_m *Controller) GetScanLog(ctx context.Context, art *artifact.Artifact, uu func (_m *Controller) GetSummary(ctx context.Context, _a1 *artifact.Artifact, mimeTypes []string) (map[string]interface{}, error) { ret := _m.Called(ctx, _a1, mimeTypes) + if len(ret) == 0 { + panic("no return value specified for GetSummary") + } + var r0 map[string]interface{} var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []string) (map[string]interface{}, error)); ok { @@ -124,6 +140,10 @@ func (_m *Controller) GetSummary(ctx context.Context, _a1 *artifact.Artifact, mi func (_m *Controller) GetVulnerable(ctx context.Context, _a1 *artifact.Artifact, allowlist models.CVESet, allowlistIsExpired bool) (*scan.Vulnerable, error) { ret := _m.Called(ctx, _a1, allowlist, allowlistIsExpired) + if len(ret) == 0 { + panic("no return value specified for GetVulnerable") + } + var r0 *scan.Vulnerable var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, models.CVESet, bool) (*scan.Vulnerable, error)); ok { @@ -157,6 +177,10 @@ func (_m *Controller) Scan(ctx context.Context, _a1 *artifact.Artifact, options _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Scan") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, ...scan.Option) error); ok { r0 = rf(ctx, _a1, options...) @@ -171,6 +195,10 @@ func (_m *Controller) Scan(ctx context.Context, _a1 *artifact.Artifact, options func (_m *Controller) ScanAll(ctx context.Context, trigger string, async bool) (int64, error) { ret := _m.Called(ctx, trigger, async) + if len(ret) == 0 { + panic("no return value specified for ScanAll") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, bool) (int64, error)); ok { @@ -195,6 +223,10 @@ func (_m *Controller) ScanAll(ctx context.Context, trigger string, async bool) ( func (_m *Controller) Stop(ctx context.Context, _a1 *artifact.Artifact, capType string) error { ret := _m.Called(ctx, _a1, capType) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, string) error); ok { r0 = rf(ctx, _a1, capType) @@ -209,6 +241,10 @@ func (_m *Controller) Stop(ctx context.Context, _a1 *artifact.Artifact, capType func (_m *Controller) StopScanAll(ctx context.Context, executionID int64, async bool) error { ret := _m.Called(ctx, executionID, async) + if len(ret) == 0 { + panic("no return value specified for StopScanAll") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, bool) error); ok { r0 = rf(ctx, executionID, async) diff --git a/src/testing/controller/scandataexport/controller.go b/src/testing/controller/scandataexport/controller.go index 08b93c74b..b75de2fc8 100644 --- a/src/testing/controller/scandataexport/controller.go +++ b/src/testing/controller/scandataexport/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scandataexport @@ -20,6 +20,10 @@ type Controller struct { func (_m *Controller) DeleteExecution(ctx context.Context, executionID int64) error { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for DeleteExecution") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, executionID) @@ -34,6 +38,10 @@ func (_m *Controller) DeleteExecution(ctx context.Context, executionID int64) er func (_m *Controller) GetExecution(ctx context.Context, executionID int64) (*export.Execution, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for GetExecution") + } + var r0 *export.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*export.Execution, error)); ok { @@ -60,6 +68,10 @@ func (_m *Controller) GetExecution(ctx context.Context, executionID int64) (*exp func (_m *Controller) GetTask(ctx context.Context, executionID int64) (*task.Task, error) { ret := _m.Called(ctx, executionID) + if len(ret) == 0 { + panic("no return value specified for GetTask") + } + var r0 *task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*task.Task, error)); ok { @@ -86,6 +98,10 @@ func (_m *Controller) GetTask(ctx context.Context, executionID int64) (*task.Tas func (_m *Controller) ListExecutions(ctx context.Context, userName string) ([]*export.Execution, error) { ret := _m.Called(ctx, userName) + if len(ret) == 0 { + panic("no return value specified for ListExecutions") + } + var r0 []*export.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]*export.Execution, error)); ok { @@ -112,6 +128,10 @@ func (_m *Controller) ListExecutions(ctx context.Context, userName string) ([]*e func (_m *Controller) Start(ctx context.Context, criteria export.Request) (int64, error) { ret := _m.Called(ctx, criteria) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, export.Request) (int64, error)); ok { diff --git a/src/testing/controller/scanner/controller.go b/src/testing/controller/scanner/controller.go index b07a82f5d..2f7f9777a 100644 --- a/src/testing/controller/scanner/controller.go +++ b/src/testing/controller/scanner/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scanner @@ -24,6 +24,10 @@ type Controller struct { func (_m *Controller) CreateRegistration(ctx context.Context, registration *scanner.Registration) (string, error) { ret := _m.Called(ctx, registration) + if len(ret) == 0 { + panic("no return value specified for CreateRegistration") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context, *scanner.Registration) (string, error)); ok { @@ -48,6 +52,10 @@ func (_m *Controller) CreateRegistration(ctx context.Context, registration *scan func (_m *Controller) DeleteRegistration(ctx context.Context, registrationUUID string) (*scanner.Registration, error) { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for DeleteRegistration") + } + var r0 *scanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*scanner.Registration, error)); ok { @@ -74,6 +82,10 @@ func (_m *Controller) DeleteRegistration(ctx context.Context, registrationUUID s func (_m *Controller) GetMetadata(ctx context.Context, registrationUUID string) (*v1.ScannerAdapterMetadata, error) { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for GetMetadata") + } + var r0 *v1.ScannerAdapterMetadata var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.ScannerAdapterMetadata, error)); ok { @@ -100,6 +112,10 @@ func (_m *Controller) GetMetadata(ctx context.Context, registrationUUID string) func (_m *Controller) GetRegistration(ctx context.Context, registrationUUID string) (*scanner.Registration, error) { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for GetRegistration") + } + var r0 *scanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*scanner.Registration, error)); ok { @@ -133,6 +149,10 @@ func (_m *Controller) GetRegistrationByProject(ctx context.Context, projectID in _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetRegistrationByProject") + } + var r0 *scanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, ...controllerscanner.Option) (*scanner.Registration, error)); ok { @@ -159,6 +179,10 @@ func (_m *Controller) GetRegistrationByProject(ctx context.Context, projectID in func (_m *Controller) GetTotalOfRegistrations(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for GetTotalOfRegistrations") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -183,6 +207,10 @@ func (_m *Controller) GetTotalOfRegistrations(ctx context.Context, query *q.Quer func (_m *Controller) ListRegistrations(ctx context.Context, query *q.Query) ([]*scanner.Registration, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListRegistrations") + } + var r0 []*scanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*scanner.Registration, error)); ok { @@ -209,6 +237,10 @@ func (_m *Controller) ListRegistrations(ctx context.Context, query *q.Query) ([] func (_m *Controller) Ping(ctx context.Context, registration *scanner.Registration) (*v1.ScannerAdapterMetadata, error) { ret := _m.Called(ctx, registration) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 *v1.ScannerAdapterMetadata var r1 error if rf, ok := ret.Get(0).(func(context.Context, *scanner.Registration) (*v1.ScannerAdapterMetadata, error)); ok { @@ -235,6 +267,10 @@ func (_m *Controller) Ping(ctx context.Context, registration *scanner.Registrati func (_m *Controller) RegistrationExists(ctx context.Context, registrationUUID string) bool { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for RegistrationExists") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, registrationUUID) @@ -249,6 +285,10 @@ func (_m *Controller) RegistrationExists(ctx context.Context, registrationUUID s func (_m *Controller) SetDefaultRegistration(ctx context.Context, registrationUUID string) error { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for SetDefaultRegistration") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, registrationUUID) @@ -263,6 +303,10 @@ func (_m *Controller) SetDefaultRegistration(ctx context.Context, registrationUU func (_m *Controller) SetRegistrationByProject(ctx context.Context, projectID int64, scannerID string) error { ret := _m.Called(ctx, projectID, scannerID) + if len(ret) == 0 { + panic("no return value specified for SetRegistrationByProject") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, projectID, scannerID) @@ -277,6 +321,10 @@ func (_m *Controller) SetRegistrationByProject(ctx context.Context, projectID in func (_m *Controller) UpdateRegistration(ctx context.Context, registration *scanner.Registration) error { ret := _m.Called(ctx, registration) + if len(ret) == 0 { + panic("no return value specified for UpdateRegistration") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *scanner.Registration) error); ok { r0 = rf(ctx, registration) diff --git a/src/testing/controller/securityhub/controller.go b/src/testing/controller/securityhub/controller.go index 55449b37e..5382fb2b1 100644 --- a/src/testing/controller/securityhub/controller.go +++ b/src/testing/controller/securityhub/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package securityhub @@ -22,6 +22,10 @@ type Controller struct { func (_m *Controller) CountVuls(ctx context.Context, scannerUUID string, projectID int64, tuneCount bool, query *q.Query) (int64, error) { ret := _m.Called(ctx, scannerUUID, projectID, tuneCount, query) + if len(ret) == 0 { + panic("no return value specified for CountVuls") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, bool, *q.Query) (int64, error)); ok { @@ -46,6 +50,10 @@ func (_m *Controller) CountVuls(ctx context.Context, scannerUUID string, project func (_m *Controller) ListVuls(ctx context.Context, scannerUUID string, projectID int64, withTag bool, query *q.Query) ([]*model.VulnerabilityItem, error) { ret := _m.Called(ctx, scannerUUID, projectID, withTag, query) + if len(ret) == 0 { + panic("no return value specified for ListVuls") + } + var r0 []*model.VulnerabilityItem var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, bool, *q.Query) ([]*model.VulnerabilityItem, error)); ok { @@ -79,6 +87,10 @@ func (_m *Controller) SecuritySummary(ctx context.Context, projectID int64, opti _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SecuritySummary") + } + var r0 *model.Summary var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, ...securityhub.Option) (*model.Summary, error)); ok { diff --git a/src/testing/controller/systemartifact/controller.go b/src/testing/controller/systemartifact/controller.go index 73c4824b4..52354277a 100644 --- a/src/testing/controller/systemartifact/controller.go +++ b/src/testing/controller/systemartifact/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package systemartifact @@ -17,6 +17,10 @@ type Controller struct { func (_m *Controller) Start(ctx context.Context, async bool, trigger string) error { ret := _m.Called(ctx, async, trigger) + if len(ret) == 0 { + panic("no return value specified for Start") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, bool, string) error); ok { r0 = rf(ctx, async, trigger) diff --git a/src/testing/controller/task/controller.go b/src/testing/controller/task/controller.go index 256b9774a..4e542513d 100644 --- a/src/testing/controller/task/controller.go +++ b/src/testing/controller/task/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -20,6 +20,10 @@ type Controller struct { func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Get(ctx context.Context, id int64) (*pkgtask.Task, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *pkgtask.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*pkgtask.Task, error)); ok { @@ -70,6 +78,10 @@ func (_m *Controller) Get(ctx context.Context, id int64) (*pkgtask.Task, error) func (_m *Controller) GetLog(ctx context.Context, id int64) ([]byte, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -96,6 +108,10 @@ func (_m *Controller) GetLog(ctx context.Context, id int64) ([]byte, error) { func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*pkgtask.Task, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*pkgtask.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*pkgtask.Task, error)); ok { @@ -122,6 +138,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query) ([]*pkgtask.Task func (_m *Controller) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) diff --git a/src/testing/controller/task/execution_controller.go b/src/testing/controller/task/execution_controller.go index 22580b6f3..5501b14de 100644 --- a/src/testing/controller/task/execution_controller.go +++ b/src/testing/controller/task/execution_controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -20,6 +20,10 @@ type ExecutionController struct { func (_m *ExecutionController) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *ExecutionController) Count(ctx context.Context, query *q.Query) (int64 func (_m *ExecutionController) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -58,6 +66,10 @@ func (_m *ExecutionController) Delete(ctx context.Context, id int64) error { func (_m *ExecutionController) Get(ctx context.Context, id int64) (*pkgtask.Execution, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *pkgtask.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*pkgtask.Execution, error)); ok { @@ -84,6 +96,10 @@ func (_m *ExecutionController) Get(ctx context.Context, id int64) (*pkgtask.Exec func (_m *ExecutionController) List(ctx context.Context, query *q.Query) ([]*pkgtask.Execution, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*pkgtask.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*pkgtask.Execution, error)); ok { @@ -110,6 +126,10 @@ func (_m *ExecutionController) List(ctx context.Context, query *q.Query) ([]*pkg func (_m *ExecutionController) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) diff --git a/src/testing/controller/user/controller.go b/src/testing/controller/user/controller.go index 17f7ea5ef..5976018d4 100644 --- a/src/testing/controller/user/controller.go +++ b/src/testing/controller/user/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package user @@ -24,6 +24,10 @@ type Controller struct { func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -48,6 +52,10 @@ func (_m *Controller) Count(ctx context.Context, query *q.Query) (int64, error) func (_m *Controller) Create(ctx context.Context, u *models.User) (int, error) { ret := _m.Called(ctx, u) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.User) (int, error)); ok { @@ -72,6 +80,10 @@ func (_m *Controller) Create(ctx context.Context, u *models.User) (int, error) { func (_m *Controller) Delete(ctx context.Context, id int) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, id) @@ -86,6 +98,10 @@ func (_m *Controller) Delete(ctx context.Context, id int) error { func (_m *Controller) Get(ctx context.Context, id int, opt *user.Option) (*models.User, error) { ret := _m.Called(ctx, id, opt) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, int, *user.Option) (*models.User, error)); ok { @@ -112,6 +128,10 @@ func (_m *Controller) Get(ctx context.Context, id int, opt *user.Option) (*model func (_m *Controller) GetByName(ctx context.Context, username string) (*models.User, error) { ret := _m.Called(ctx, username) + if len(ret) == 0 { + panic("no return value specified for GetByName") + } + var r0 *models.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*models.User, error)); ok { @@ -138,6 +158,10 @@ func (_m *Controller) GetByName(ctx context.Context, username string) (*models.U func (_m *Controller) GetBySubIss(ctx context.Context, sub string, iss string) (*models.User, error) { ret := _m.Called(ctx, sub, iss) + if len(ret) == 0 { + panic("no return value specified for GetBySubIss") + } + var r0 *models.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (*models.User, error)); ok { @@ -171,6 +195,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...userm _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, ...usermodels.Option) ([]*models.User, error)); ok { @@ -197,6 +225,10 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, options ...userm func (_m *Controller) OnboardOIDCUser(ctx context.Context, u *models.User) error { ret := _m.Called(ctx, u) + if len(ret) == 0 { + panic("no return value specified for OnboardOIDCUser") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.User) error); ok { r0 = rf(ctx, u) @@ -211,6 +243,10 @@ func (_m *Controller) OnboardOIDCUser(ctx context.Context, u *models.User) error func (_m *Controller) SetCliSecret(ctx context.Context, id int, secret string) error { ret := _m.Called(ctx, id, secret) + if len(ret) == 0 { + panic("no return value specified for SetCliSecret") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok { r0 = rf(ctx, id, secret) @@ -225,6 +261,10 @@ func (_m *Controller) SetCliSecret(ctx context.Context, id int, secret string) e func (_m *Controller) SetSysAdmin(ctx context.Context, id int, adminFlag bool) error { ret := _m.Called(ctx, id, adminFlag) + if len(ret) == 0 { + panic("no return value specified for SetSysAdmin") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, bool) error); ok { r0 = rf(ctx, id, adminFlag) @@ -246,6 +286,10 @@ func (_m *Controller) UpdateOIDCMeta(ctx context.Context, ou *models.OIDCUser, c _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateOIDCMeta") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.OIDCUser, ...string) error); ok { r0 = rf(ctx, ou, cols...) @@ -260,6 +304,10 @@ func (_m *Controller) UpdateOIDCMeta(ctx context.Context, ou *models.OIDCUser, c func (_m *Controller) UpdatePassword(ctx context.Context, id int, password string) error { ret := _m.Called(ctx, id, password) + if len(ret) == 0 { + panic("no return value specified for UpdatePassword") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok { r0 = rf(ctx, id, password) @@ -281,6 +329,10 @@ func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User, cols .. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateProfile") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok { r0 = rf(ctx, u, cols...) @@ -295,6 +347,10 @@ func (_m *Controller) UpdateProfile(ctx context.Context, u *models.User, cols .. func (_m *Controller) VerifyPassword(ctx context.Context, usernameOrEmail string, password string) (bool, error) { ret := _m.Called(ctx, usernameOrEmail, password) + if len(ret) == 0 { + panic("no return value specified for VerifyPassword") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (bool, error)); ok { diff --git a/src/testing/controller/webhook/controller.go b/src/testing/controller/webhook/controller.go index a956dc7f1..c50d44850 100644 --- a/src/testing/controller/webhook/controller.go +++ b/src/testing/controller/webhook/controller.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package webhook @@ -24,6 +24,10 @@ type Controller struct { func (_m *Controller) CountExecutions(ctx context.Context, policyID int64, query *q.Query) (int64, error) { ret := _m.Called(ctx, policyID, query) + if len(ret) == 0 { + panic("no return value specified for CountExecutions") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) (int64, error)); ok { @@ -48,6 +52,10 @@ func (_m *Controller) CountExecutions(ctx context.Context, policyID int64, query func (_m *Controller) CountPolicies(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for CountPolicies") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -72,6 +80,10 @@ func (_m *Controller) CountPolicies(ctx context.Context, query *q.Query) (int64, func (_m *Controller) CountTasks(ctx context.Context, execID int64, query *q.Query) (int64, error) { ret := _m.Called(ctx, execID, query) + if len(ret) == 0 { + panic("no return value specified for CountTasks") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) (int64, error)); ok { @@ -96,6 +108,10 @@ func (_m *Controller) CountTasks(ctx context.Context, execID int64, query *q.Que func (_m *Controller) CreatePolicy(ctx context.Context, policy *model.Policy) (int64, error) { ret := _m.Called(ctx, policy) + if len(ret) == 0 { + panic("no return value specified for CreatePolicy") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -120,6 +136,10 @@ func (_m *Controller) CreatePolicy(ctx context.Context, policy *model.Policy) (i func (_m *Controller) DeletePolicy(ctx context.Context, policyID int64) error { ret := _m.Called(ctx, policyID) + if len(ret) == 0 { + panic("no return value specified for DeletePolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, policyID) @@ -134,6 +154,10 @@ func (_m *Controller) DeletePolicy(ctx context.Context, policyID int64) error { func (_m *Controller) GetLastTriggerTime(ctx context.Context, eventType string, policyID int64) (time.Time, error) { ret := _m.Called(ctx, eventType, policyID) + if len(ret) == 0 { + panic("no return value specified for GetLastTriggerTime") + } + var r0 time.Time var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) (time.Time, error)); ok { @@ -158,6 +182,10 @@ func (_m *Controller) GetLastTriggerTime(ctx context.Context, eventType string, func (_m *Controller) GetPolicy(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetPolicy") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -184,6 +212,10 @@ func (_m *Controller) GetPolicy(ctx context.Context, id int64) (*model.Policy, e func (_m *Controller) GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) { ret := _m.Called(ctx, projectID, eventType) + if len(ret) == 0 { + panic("no return value specified for GetRelatedPolices") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) ([]*model.Policy, error)); ok { @@ -210,6 +242,10 @@ func (_m *Controller) GetRelatedPolices(ctx context.Context, projectID int64, ev func (_m *Controller) GetTask(ctx context.Context, taskID int64) (*task.Task, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetTask") + } + var r0 *task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*task.Task, error)); ok { @@ -236,6 +272,10 @@ func (_m *Controller) GetTask(ctx context.Context, taskID int64) (*task.Task, er func (_m *Controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, error) { ret := _m.Called(ctx, taskID) + if len(ret) == 0 { + panic("no return value specified for GetTaskLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -262,6 +302,10 @@ func (_m *Controller) GetTaskLog(ctx context.Context, taskID int64) ([]byte, err func (_m *Controller) ListExecutions(ctx context.Context, policyID int64, query *q.Query) ([]*task.Execution, error) { ret := _m.Called(ctx, policyID, query) + if len(ret) == 0 { + panic("no return value specified for ListExecutions") + } + var r0 []*task.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) ([]*task.Execution, error)); ok { @@ -288,6 +332,10 @@ func (_m *Controller) ListExecutions(ctx context.Context, policyID int64, query func (_m *Controller) ListPolicies(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListPolicies") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -314,6 +362,10 @@ func (_m *Controller) ListPolicies(ctx context.Context, query *q.Query) ([]*mode func (_m *Controller) ListTasks(ctx context.Context, execID int64, query *q.Query) ([]*task.Task, error) { ret := _m.Called(ctx, execID, query) + if len(ret) == 0 { + panic("no return value specified for ListTasks") + } + var r0 []*task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) ([]*task.Task, error)); ok { @@ -340,6 +392,10 @@ func (_m *Controller) ListTasks(ctx context.Context, execID int64, query *q.Quer func (_m *Controller) UpdatePolicy(ctx context.Context, policy *model.Policy) error { ret := _m.Called(ctx, policy) + if len(ret) == 0 { + panic("no return value specified for UpdatePolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) error); ok { r0 = rf(ctx, policy) diff --git a/src/testing/lib/cache/cache.go b/src/testing/lib/cache/cache.go index e28a1040a..e4e719d9b 100644 --- a/src/testing/lib/cache/cache.go +++ b/src/testing/lib/cache/cache.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package cache @@ -21,6 +21,10 @@ type Cache struct { func (_m *Cache) Contains(ctx context.Context, key string) bool { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Contains") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, key) @@ -35,6 +39,10 @@ func (_m *Cache) Contains(ctx context.Context, key string) bool { func (_m *Cache) Delete(ctx context.Context, key string) error { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, key) @@ -49,6 +57,10 @@ func (_m *Cache) Delete(ctx context.Context, key string) error { func (_m *Cache) Fetch(ctx context.Context, key string, value interface{}) error { ret := _m.Called(ctx, key, value) + if len(ret) == 0 { + panic("no return value specified for Fetch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { r0 = rf(ctx, key, value) @@ -63,6 +75,10 @@ func (_m *Cache) Fetch(ctx context.Context, key string, value interface{}) error func (_m *Cache) Ping(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -84,6 +100,10 @@ func (_m *Cache) Save(ctx context.Context, key string, value interface{}, expira _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Save") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, interface{}, ...time.Duration) error); ok { r0 = rf(ctx, key, value, expiration...) @@ -98,6 +118,10 @@ func (_m *Cache) Save(ctx context.Context, key string, value interface{}, expira func (_m *Cache) Scan(ctx context.Context, match string) (cache.Iterator, error) { ret := _m.Called(ctx, match) + if len(ret) == 0 { + panic("no return value specified for Scan") + } + var r0 cache.Iterator var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (cache.Iterator, error)); ok { diff --git a/src/testing/lib/cache/iterator.go b/src/testing/lib/cache/iterator.go index ec7dc6f08..7a80fb382 100644 --- a/src/testing/lib/cache/iterator.go +++ b/src/testing/lib/cache/iterator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package cache @@ -17,6 +17,10 @@ type Iterator struct { func (_m *Iterator) Next(ctx context.Context) bool { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Next") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context) bool); ok { r0 = rf(ctx) @@ -31,6 +35,10 @@ func (_m *Iterator) Next(ctx context.Context) bool { func (_m *Iterator) Val() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Val") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/src/testing/lib/config/manager.go b/src/testing/lib/config/manager.go index f04086964..ff6456b5d 100644 --- a/src/testing/lib/config/manager.go +++ b/src/testing/lib/config/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package config @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Get(ctx context.Context, key string) *metadata.ConfigureValue { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *metadata.ConfigureValue if rf, ok := ret.Get(0).(func(context.Context, string) *metadata.ConfigureValue); ok { r0 = rf(ctx, key) @@ -36,6 +40,10 @@ func (_m *Manager) Get(ctx context.Context, key string) *metadata.ConfigureValue func (_m *Manager) GetAll(ctx context.Context) map[string]interface{} { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAll") + } + var r0 map[string]interface{} if rf, ok := ret.Get(0).(func(context.Context) map[string]interface{}); ok { r0 = rf(ctx) @@ -52,6 +60,10 @@ func (_m *Manager) GetAll(ctx context.Context) map[string]interface{} { func (_m *Manager) GetDatabaseCfg() *models.Database { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetDatabaseCfg") + } + var r0 *models.Database if rf, ok := ret.Get(0).(func() *models.Database); ok { r0 = rf() @@ -68,6 +80,10 @@ func (_m *Manager) GetDatabaseCfg() *models.Database { func (_m *Manager) GetUserCfgs(ctx context.Context) map[string]interface{} { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetUserCfgs") + } + var r0 map[string]interface{} if rf, ok := ret.Get(0).(func(context.Context) map[string]interface{}); ok { r0 = rf(ctx) @@ -84,6 +100,10 @@ func (_m *Manager) GetUserCfgs(ctx context.Context) map[string]interface{} { func (_m *Manager) Load(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Load") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -98,6 +118,10 @@ func (_m *Manager) Load(ctx context.Context) error { func (_m *Manager) Save(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Save") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -117,6 +141,10 @@ func (_m *Manager) Set(ctx context.Context, key string, value interface{}) { func (_m *Manager) UpdateConfig(ctx context.Context, cfgs map[string]interface{}) error { ret := _m.Called(ctx, cfgs) + if len(ret) == 0 { + panic("no return value specified for UpdateConfig") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) error); ok { r0 = rf(ctx, cfgs) @@ -131,6 +159,10 @@ func (_m *Manager) UpdateConfig(ctx context.Context, cfgs map[string]interface{} func (_m *Manager) ValidateCfg(ctx context.Context, cfgs map[string]interface{}) error { ret := _m.Called(ctx, cfgs) + if len(ret) == 0 { + panic("no return value specified for ValidateCfg") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, map[string]interface{}) error); ok { r0 = rf(ctx, cfgs) diff --git a/src/testing/lib/orm/creator.go b/src/testing/lib/orm/creator.go index 7844ecaf2..d10e5f918 100644 --- a/src/testing/lib/orm/creator.go +++ b/src/testing/lib/orm/creator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package orm @@ -16,6 +16,10 @@ type Creator struct { func (_m *Creator) Create() orm.Ormer { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 orm.Ormer if rf, ok := ret.Get(0).(func() orm.Ormer); ok { r0 = rf() diff --git a/src/testing/pkg/accessory/dao/dao.go b/src/testing/pkg/accessory/dao/dao.go index 886ee4f14..d4165cf9b 100644 --- a/src/testing/pkg/accessory/dao/dao.go +++ b/src/testing/pkg/accessory/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -20,6 +20,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, accessory *dao.Accessory) (int64, error) { ret := _m.Called(ctx, accessory) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Accessory) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *DAO) Create(ctx context.Context, accessory *dao.Accessory) (int64, err func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) DeleteAccessories(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for DeleteAccessories") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -106,6 +122,10 @@ func (_m *DAO) DeleteAccessories(ctx context.Context, query *q.Query) (int64, er func (_m *DAO) Get(ctx context.Context, id int64) (*dao.Accessory, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *dao.Accessory var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Accessory, error)); ok { @@ -132,6 +152,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*dao.Accessory, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*dao.Accessory, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*dao.Accessory var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Accessory, error)); ok { @@ -158,6 +182,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*dao.Accessory, erro func (_m *DAO) Update(ctx context.Context, accessory *dao.Accessory) error { ret := _m.Called(ctx, accessory) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Accessory) error); ok { r0 = rf(ctx, accessory) diff --git a/src/testing/pkg/accessory/manager.go b/src/testing/pkg/accessory/manager.go index 9a3312a32..1245311d8 100644 --- a/src/testing/pkg/accessory/manager.go +++ b/src/testing/pkg/accessory/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package accessory @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 model.AccessoryData) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, model.AccessoryData) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 model.AccessoryData) (int64, func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) DeleteAccessories(ctx context.Context, _a1 *q.Query) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for DeleteAccessories") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) error); ok { r0 = rf(ctx, _a1) @@ -96,6 +112,10 @@ func (_m *Manager) DeleteAccessories(ctx context.Context, _a1 *q.Query) error { func (_m *Manager) Ensure(ctx context.Context, subArtDigest string, subArtRepo string, subArtID int64, artifactID int64, size int64, digest string, accType string) error { ret := _m.Called(ctx, subArtDigest, subArtRepo, subArtID, artifactID, size, digest, accType) + if len(ret) == 0 { + panic("no return value specified for Ensure") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, int64, int64, int64, string, string) error); ok { r0 = rf(ctx, subArtDigest, subArtRepo, subArtID, artifactID, size, digest, accType) @@ -110,6 +130,10 @@ func (_m *Manager) Ensure(ctx context.Context, subArtDigest string, subArtRepo s func (_m *Manager) Get(ctx context.Context, id int64) (model.Accessory, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 model.Accessory var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (model.Accessory, error)); ok { @@ -136,6 +160,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (model.Accessory, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]model.Accessory, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []model.Accessory var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]model.Accessory, error)); ok { @@ -162,6 +190,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]model.Accessory, func (_m *Manager) Update(ctx context.Context, _a1 model.AccessoryData) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, model.AccessoryData) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/pkg/accessory/model/accessory.go b/src/testing/pkg/accessory/model/accessory.go index 04d4dad65..a33371896 100644 --- a/src/testing/pkg/accessory/model/accessory.go +++ b/src/testing/pkg/accessory/model/accessory.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package model @@ -16,6 +16,10 @@ type Accessory struct { func (_m *Accessory) Display() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Display") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *Accessory) Display() bool { func (_m *Accessory) GetData() model.AccessoryData { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetData") + } + var r0 model.AccessoryData if rf, ok := ret.Get(0).(func() model.AccessoryData); ok { r0 = rf() @@ -44,6 +52,10 @@ func (_m *Accessory) GetData() model.AccessoryData { func (_m *Accessory) IsHard() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsHard") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -58,6 +70,10 @@ func (_m *Accessory) IsHard() bool { func (_m *Accessory) IsSoft() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for IsSoft") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -72,6 +88,10 @@ func (_m *Accessory) IsSoft() bool { func (_m *Accessory) Kind() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Kind") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() diff --git a/src/testing/pkg/allowlist/dao/dao.go b/src/testing/pkg/allowlist/dao/dao.go index d818c5e57..a8ebfec10 100644 --- a/src/testing/pkg/allowlist/dao/dao.go +++ b/src/testing/pkg/allowlist/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -19,6 +19,10 @@ type DAO struct { func (_m *DAO) QueryByProjectID(ctx context.Context, pid int64) (*models.CVEAllowlist, error) { ret := _m.Called(ctx, pid) + if len(ret) == 0 { + panic("no return value specified for QueryByProjectID") + } + var r0 *models.CVEAllowlist var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*models.CVEAllowlist, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) QueryByProjectID(ctx context.Context, pid int64) (*models.CVEAllo func (_m *DAO) Set(ctx context.Context, l models.CVEAllowlist) (int64, error) { ret := _m.Called(ctx, l) + if len(ret) == 0 { + panic("no return value specified for Set") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, models.CVEAllowlist) (int64, error)); ok { diff --git a/src/testing/pkg/allowlist/manager.go b/src/testing/pkg/allowlist/manager.go index a3c7dc30e..ecb1b4115 100644 --- a/src/testing/pkg/allowlist/manager.go +++ b/src/testing/pkg/allowlist/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package robot @@ -18,6 +18,10 @@ type Manager struct { func (_m *Manager) CreateEmpty(ctx context.Context, projectID int64) error { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for CreateEmpty") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, projectID) @@ -32,6 +36,10 @@ func (_m *Manager) CreateEmpty(ctx context.Context, projectID int64) error { func (_m *Manager) Get(ctx context.Context, projectID int64) (*models.CVEAllowlist, error) { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.CVEAllowlist var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*models.CVEAllowlist, error)); ok { @@ -58,6 +66,10 @@ func (_m *Manager) Get(ctx context.Context, projectID int64) (*models.CVEAllowli func (_m *Manager) GetSys(ctx context.Context) (*models.CVEAllowlist, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetSys") + } + var r0 *models.CVEAllowlist var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*models.CVEAllowlist, error)); ok { @@ -84,6 +96,10 @@ func (_m *Manager) GetSys(ctx context.Context) (*models.CVEAllowlist, error) { func (_m *Manager) Set(ctx context.Context, projectID int64, list models.CVEAllowlist) error { ret := _m.Called(ctx, projectID, list) + if len(ret) == 0 { + panic("no return value specified for Set") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, models.CVEAllowlist) error); ok { r0 = rf(ctx, projectID, list) @@ -98,6 +114,10 @@ func (_m *Manager) Set(ctx context.Context, projectID int64, list models.CVEAllo func (_m *Manager) SetSys(ctx context.Context, list models.CVEAllowlist) error { ret := _m.Called(ctx, list) + if len(ret) == 0 { + panic("no return value specified for SetSys") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, models.CVEAllowlist) error); ok { r0 = rf(ctx, list) diff --git a/src/testing/pkg/artifact/manager.go b/src/testing/pkg/artifact/manager.go index fb246aebd..bf19a0b60 100644 --- a/src/testing/pkg/artifact/manager.go +++ b/src/testing/pkg/artifact/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package artifact @@ -23,6 +23,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -47,6 +51,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *artifact.Artifact) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact) (int64, error)); ok { @@ -71,6 +79,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *artifact.Artifact) (int64, e func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -85,6 +97,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) DeleteReference(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteReference") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -99,6 +115,10 @@ func (_m *Manager) DeleteReference(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*artifact.Artifact, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*artifact.Artifact, error)); ok { @@ -125,6 +145,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*artifact.Artifact, error func (_m *Manager) GetByDigest(ctx context.Context, repository string, digest string) (*artifact.Artifact, error) { ret := _m.Called(ctx, repository, digest) + if len(ret) == 0 { + panic("no return value specified for GetByDigest") + } + var r0 *artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (*artifact.Artifact, error)); ok { @@ -151,6 +175,10 @@ func (_m *Manager) GetByDigest(ctx context.Context, repository string, digest st func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*artifact.Artifact, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*artifact.Artifact, error)); ok { @@ -177,6 +205,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*artifact.Artifa func (_m *Manager) ListReferences(ctx context.Context, query *q.Query) ([]*artifact.Reference, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListReferences") + } + var r0 []*artifact.Reference var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*artifact.Reference, error)); ok { @@ -210,6 +242,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *artifact.Artifact, props ... _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, ...string) error); ok { r0 = rf(ctx, _a1, props...) @@ -224,6 +260,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *artifact.Artifact, props ... func (_m *Manager) UpdatePullTime(ctx context.Context, id int64, pullTime time.Time) error { ret := _m.Called(ctx, id, pullTime) + if len(ret) == 0 { + panic("no return value specified for UpdatePullTime") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, time.Time) error); ok { r0 = rf(ctx, id, pullTime) diff --git a/src/testing/pkg/audit/dao/dao.go b/src/testing/pkg/audit/dao/dao.go index 97665fdaa..74dd03aaa 100644 --- a/src/testing/pkg/audit/dao/dao.go +++ b/src/testing/pkg/audit/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, access *model.AuditLog) (int64, error) { ret := _m.Called(ctx, access) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.AuditLog) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, access *model.AuditLog) (int64, error func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*model.AuditLog, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.AuditLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.AuditLog, error)); ok { @@ -109,6 +125,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.AuditLog, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.AuditLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.AuditLog, error)); ok { @@ -135,6 +155,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, err func (_m *DAO) Purge(ctx context.Context, retentionHour int, includeOperations []string, dryRun bool) (int64, error) { ret := _m.Called(ctx, retentionHour, includeOperations, dryRun) + if len(ret) == 0 { + panic("no return value specified for Purge") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int, []string, bool) (int64, error)); ok { @@ -159,6 +183,10 @@ func (_m *DAO) Purge(ctx context.Context, retentionHour int, includeOperations [ func (_m *DAO) UpdateUsername(ctx context.Context, username string, usernameReplace string) error { ret := _m.Called(ctx, username, usernameReplace) + if len(ret) == 0 { + panic("no return value specified for UpdateUsername") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { r0 = rf(ctx, username, usernameReplace) diff --git a/src/testing/pkg/audit/manager.go b/src/testing/pkg/audit/manager.go index 1efcd06df..887996c0b 100644 --- a/src/testing/pkg/audit/manager.go +++ b/src/testing/pkg/audit/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package audit @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *model.AuditLog) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.AuditLog) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *model.AuditLog) (int64, erro func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.AuditLog, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.AuditLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.AuditLog, error)); ok { @@ -108,6 +124,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.AuditLog, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.AuditLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.AuditLog, error)); ok { @@ -134,6 +154,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.AuditLog, func (_m *Manager) Purge(ctx context.Context, retentionHour int, includeOperations []string, dryRun bool) (int64, error) { ret := _m.Called(ctx, retentionHour, includeOperations, dryRun) + if len(ret) == 0 { + panic("no return value specified for Purge") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int, []string, bool) (int64, error)); ok { @@ -158,6 +182,10 @@ func (_m *Manager) Purge(ctx context.Context, retentionHour int, includeOperatio func (_m *Manager) UpdateUsername(ctx context.Context, username string, replaceWith string) error { ret := _m.Called(ctx, username, replaceWith) + if len(ret) == 0 { + panic("no return value specified for UpdateUsername") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { r0 = rf(ctx, username, replaceWith) diff --git a/src/testing/pkg/blob/manager.go b/src/testing/pkg/blob/manager.go index 3edba7fbb..8308e4aaf 100644 --- a/src/testing/pkg/blob/manager.go +++ b/src/testing/pkg/blob/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package blob @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) AssociateWithArtifact(ctx context.Context, blobDigest string, artifactDigest string) (int64, error) { ret := _m.Called(ctx, blobDigest, artifactDigest) + if len(ret) == 0 { + panic("no return value specified for AssociateWithArtifact") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) AssociateWithArtifact(ctx context.Context, blobDigest string, func (_m *Manager) AssociateWithProject(ctx context.Context, blobID int64, projectID int64) (int64, error) { ret := _m.Called(ctx, blobID, projectID) + if len(ret) == 0 { + panic("no return value specified for AssociateWithProject") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) AssociateWithProject(ctx context.Context, blobID int64, proje func (_m *Manager) CalculateTotalSize(ctx context.Context, excludeForeignLayer bool) (int64, error) { ret := _m.Called(ctx, excludeForeignLayer) + if len(ret) == 0 { + panic("no return value specified for CalculateTotalSize") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, bool) (int64, error)); ok { @@ -92,6 +104,10 @@ func (_m *Manager) CalculateTotalSize(ctx context.Context, excludeForeignLayer b func (_m *Manager) CalculateTotalSizeByProject(ctx context.Context, projectID int64, excludeForeignLayer bool) (int64, error) { ret := _m.Called(ctx, projectID, excludeForeignLayer) + if len(ret) == 0 { + panic("no return value specified for CalculateTotalSizeByProject") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, bool) (int64, error)); ok { @@ -116,6 +132,10 @@ func (_m *Manager) CalculateTotalSizeByProject(ctx context.Context, projectID in func (_m *Manager) CleanupAssociationsForArtifact(ctx context.Context, artifactDigest string) error { ret := _m.Called(ctx, artifactDigest) + if len(ret) == 0 { + panic("no return value specified for CleanupAssociationsForArtifact") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, artifactDigest) @@ -130,6 +150,10 @@ func (_m *Manager) CleanupAssociationsForArtifact(ctx context.Context, artifactD func (_m *Manager) CleanupAssociationsForProject(ctx context.Context, projectID int64, blobs []*models.Blob) error { ret := _m.Called(ctx, projectID, blobs) + if len(ret) == 0 { + panic("no return value specified for CleanupAssociationsForProject") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, []*models.Blob) error); ok { r0 = rf(ctx, projectID, blobs) @@ -144,6 +168,10 @@ func (_m *Manager) CleanupAssociationsForProject(ctx context.Context, projectID func (_m *Manager) Create(ctx context.Context, digest string, contentType string, size int64) (int64, error) { ret := _m.Called(ctx, digest, contentType, size) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, int64) (int64, error)); ok { @@ -168,6 +196,10 @@ func (_m *Manager) Create(ctx context.Context, digest string, contentType string func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -182,6 +214,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) FindBlobsShouldUnassociatedWithProject(ctx context.Context, projectID int64, blobs []*models.Blob) ([]*models.Blob, error) { ret := _m.Called(ctx, projectID, blobs) + if len(ret) == 0 { + panic("no return value specified for FindBlobsShouldUnassociatedWithProject") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, []*models.Blob) ([]*models.Blob, error)); ok { @@ -208,6 +244,10 @@ func (_m *Manager) FindBlobsShouldUnassociatedWithProject(ctx context.Context, p func (_m *Manager) Get(ctx context.Context, digest string) (*models.Blob, error) { ret := _m.Called(ctx, digest) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*models.Blob, error)); ok { @@ -234,6 +274,10 @@ func (_m *Manager) Get(ctx context.Context, digest string) (*models.Blob, error) func (_m *Manager) GetByArt(ctx context.Context, digest string) ([]*models.Blob, error) { ret := _m.Called(ctx, digest) + if len(ret) == 0 { + panic("no return value specified for GetByArt") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]*models.Blob, error)); ok { @@ -260,6 +304,10 @@ func (_m *Manager) GetByArt(ctx context.Context, digest string) ([]*models.Blob, func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Blob, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.Blob, error)); ok { @@ -286,6 +334,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Blob, er func (_m *Manager) Update(ctx context.Context, _a1 *models.Blob) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) error); ok { r0 = rf(ctx, _a1) @@ -300,6 +352,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *models.Blob) error { func (_m *Manager) UpdateBlobStatus(ctx context.Context, _a1 *models.Blob) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for UpdateBlobStatus") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.Blob) (int64, error)); ok { @@ -324,6 +380,10 @@ func (_m *Manager) UpdateBlobStatus(ctx context.Context, _a1 *models.Blob) (int6 func (_m *Manager) UselessBlobs(ctx context.Context, timeWindowHours int64) ([]*models.Blob, error) { ret := _m.Called(ctx, timeWindowHours) + if len(ret) == 0 { + panic("no return value specified for UselessBlobs") + } + var r0 []*models.Blob var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*models.Blob, error)); ok { diff --git a/src/testing/pkg/cached/manifest/redis/cached_manager.go b/src/testing/pkg/cached/manifest/redis/cached_manager.go index 6ae99bb96..033e96aca 100644 --- a/src/testing/pkg/cached/manifest/redis/cached_manager.go +++ b/src/testing/pkg/cached/manifest/redis/cached_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package redis @@ -19,6 +19,10 @@ type CachedManager struct { func (_m *CachedManager) CacheClient(ctx context.Context) cache.Cache { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for CacheClient") + } + var r0 cache.Cache if rf, ok := ret.Get(0).(func(context.Context) cache.Cache); ok { r0 = rf(ctx) @@ -35,6 +39,10 @@ func (_m *CachedManager) CacheClient(ctx context.Context) cache.Cache { func (_m *CachedManager) CountCache(ctx context.Context) (int64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for CountCache") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok { @@ -59,6 +67,10 @@ func (_m *CachedManager) CountCache(ctx context.Context) (int64, error) { func (_m *CachedManager) Delete(ctx context.Context, digest string) error { ret := _m.Called(ctx, digest) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, digest) @@ -73,6 +85,10 @@ func (_m *CachedManager) Delete(ctx context.Context, digest string) error { func (_m *CachedManager) DeleteCache(ctx context.Context, key string) error { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for DeleteCache") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, key) @@ -87,6 +103,10 @@ func (_m *CachedManager) DeleteCache(ctx context.Context, key string) error { func (_m *CachedManager) FlushAll(ctx context.Context) error { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for FlushAll") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context) error); ok { r0 = rf(ctx) @@ -101,6 +121,10 @@ func (_m *CachedManager) FlushAll(ctx context.Context) error { func (_m *CachedManager) Get(ctx context.Context, digest string) ([]byte, error) { ret := _m.Called(ctx, digest) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]byte, error)); ok { @@ -127,6 +151,10 @@ func (_m *CachedManager) Get(ctx context.Context, digest string) ([]byte, error) func (_m *CachedManager) ResourceType(ctx context.Context) string { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ResourceType") + } + var r0 string if rf, ok := ret.Get(0).(func(context.Context) string); ok { r0 = rf(ctx) @@ -141,6 +169,10 @@ func (_m *CachedManager) ResourceType(ctx context.Context) string { func (_m *CachedManager) Save(ctx context.Context, digest string, manifest []byte) error { ret := _m.Called(ctx, digest, manifest) + if len(ret) == 0 { + panic("no return value specified for Save") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, []byte) error); ok { r0 = rf(ctx, digest, manifest) diff --git a/src/testing/pkg/immutable/dao/dao.go b/src/testing/pkg/immutable/dao/dao.go index a3e841e6f..8ad5aac0f 100644 --- a/src/testing/pkg/immutable/dao/dao.go +++ b/src/testing/pkg/immutable/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) CreateImmutableRule(ctx context.Context, ir *model.ImmutableRule) (int64, error) { ret := _m.Called(ctx, ir) + if len(ret) == 0 { + panic("no return value specified for CreateImmutableRule") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.ImmutableRule) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) CreateImmutableRule(ctx context.Context, ir *model.ImmutableRule) func (_m *DAO) DeleteImmutableRule(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteImmutableRule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) DeleteImmutableRule(ctx context.Context, id int64) error { func (_m *DAO) GetImmutableRule(ctx context.Context, id int64) (*model.ImmutableRule, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetImmutableRule") + } + var r0 *model.ImmutableRule var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.ImmutableRule, error)); ok { @@ -109,6 +125,10 @@ func (_m *DAO) GetImmutableRule(ctx context.Context, id int64) (*model.Immutable func (_m *DAO) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model.ImmutableRule, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListImmutableRules") + } + var r0 []*model.ImmutableRule var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.ImmutableRule, error)); ok { @@ -135,6 +155,10 @@ func (_m *DAO) ListImmutableRules(ctx context.Context, query *q.Query) ([]*model func (_m *DAO) ToggleImmutableRule(ctx context.Context, id int64, status bool) error { ret := _m.Called(ctx, id, status) + if len(ret) == 0 { + panic("no return value specified for ToggleImmutableRule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, bool) error); ok { r0 = rf(ctx, id, status) @@ -149,6 +173,10 @@ func (_m *DAO) ToggleImmutableRule(ctx context.Context, id int64, status bool) e func (_m *DAO) UpdateImmutableRule(ctx context.Context, projectID int64, ir *model.ImmutableRule) error { ret := _m.Called(ctx, projectID, ir) + if len(ret) == 0 { + panic("no return value specified for UpdateImmutableRule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, *model.ImmutableRule) error); ok { r0 = rf(ctx, projectID, ir) diff --git a/src/testing/pkg/joblog/dao/dao.go b/src/testing/pkg/joblog/dao/dao.go index 37fd4b9b0..353d2c168 100644 --- a/src/testing/pkg/joblog/dao/dao.go +++ b/src/testing/pkg/joblog/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Create(ctx context.Context, jobLog *models.JobLog) (int64, error) { ret := _m.Called(ctx, jobLog) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.JobLog) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Create(ctx context.Context, jobLog *models.JobLog) (int64, error) func (_m *DAO) DeleteBefore(ctx context.Context, t time.Time) (int64, error) { ret := _m.Called(ctx, t) + if len(ret) == 0 { + panic("no return value specified for DeleteBefore") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, time.Time) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) DeleteBefore(ctx context.Context, t time.Time) (int64, error) { func (_m *DAO) Get(ctx context.Context, uuid string) (*models.JobLog, error) { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.JobLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*models.JobLog, error)); ok { diff --git a/src/testing/pkg/joblog/manager.go b/src/testing/pkg/joblog/manager.go index 103fb0734..d00e413c9 100644 --- a/src/testing/pkg/joblog/manager.go +++ b/src/testing/pkg/joblog/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package joblog @@ -21,6 +21,10 @@ type Manager struct { func (_m *Manager) Create(ctx context.Context, jobLog *models.JobLog) (int64, error) { ret := _m.Called(ctx, jobLog) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.JobLog) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *Manager) Create(ctx context.Context, jobLog *models.JobLog) (int64, er func (_m *Manager) DeleteBefore(ctx context.Context, t time.Time) (int64, error) { ret := _m.Called(ctx, t) + if len(ret) == 0 { + panic("no return value specified for DeleteBefore") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, time.Time) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *Manager) DeleteBefore(ctx context.Context, t time.Time) (int64, error) func (_m *Manager) Get(ctx context.Context, uuid string) (*models.JobLog, error) { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.JobLog var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*models.JobLog, error)); ok { diff --git a/src/testing/pkg/jobmonitor/job_service_monitor_client.go b/src/testing/pkg/jobmonitor/job_service_monitor_client.go index 569357d4b..d528776fc 100644 --- a/src/testing/pkg/jobmonitor/job_service_monitor_client.go +++ b/src/testing/pkg/jobmonitor/job_service_monitor_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobmonitor @@ -16,6 +16,10 @@ type JobServiceMonitorClient struct { func (_m *JobServiceMonitorClient) Queues() ([]*work.Queue, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Queues") + } + var r0 []*work.Queue var r1 error if rf, ok := ret.Get(0).(func() ([]*work.Queue, error)); ok { @@ -42,6 +46,10 @@ func (_m *JobServiceMonitorClient) Queues() ([]*work.Queue, error) { func (_m *JobServiceMonitorClient) WorkerObservations() ([]*work.WorkerObservation, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for WorkerObservations") + } + var r0 []*work.WorkerObservation var r1 error if rf, ok := ret.Get(0).(func() ([]*work.WorkerObservation, error)); ok { @@ -68,6 +76,10 @@ func (_m *JobServiceMonitorClient) WorkerObservations() ([]*work.WorkerObservati func (_m *JobServiceMonitorClient) WorkerPoolHeartbeats() ([]*work.WorkerPoolHeartbeat, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for WorkerPoolHeartbeats") + } + var r0 []*work.WorkerPoolHeartbeat var r1 error if rf, ok := ret.Get(0).(func() ([]*work.WorkerPoolHeartbeat, error)); ok { diff --git a/src/testing/pkg/jobmonitor/pool_manager.go b/src/testing/pkg/jobmonitor/pool_manager.go index 7b41ac6d8..51dc4fbf9 100644 --- a/src/testing/pkg/jobmonitor/pool_manager.go +++ b/src/testing/pkg/jobmonitor/pool_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobmonitor @@ -18,6 +18,10 @@ type PoolManager struct { func (_m *PoolManager) List(ctx context.Context, monitorClient jobmonitor.JobServiceMonitorClient) ([]*jobmonitor.WorkerPool, error) { ret := _m.Called(ctx, monitorClient) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*jobmonitor.WorkerPool var r1 error if rf, ok := ret.Get(0).(func(context.Context, jobmonitor.JobServiceMonitorClient) ([]*jobmonitor.WorkerPool, error)); ok { diff --git a/src/testing/pkg/jobmonitor/queue_manager.go b/src/testing/pkg/jobmonitor/queue_manager.go index de7862bae..bce10ef38 100644 --- a/src/testing/pkg/jobmonitor/queue_manager.go +++ b/src/testing/pkg/jobmonitor/queue_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobmonitor @@ -18,6 +18,10 @@ type QueueManager struct { func (_m *QueueManager) List(ctx context.Context, monitClient jobmonitor.JobServiceMonitorClient) ([]*jobmonitor.Queue, error) { ret := _m.Called(ctx, monitClient) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*jobmonitor.Queue var r1 error if rf, ok := ret.Get(0).(func(context.Context, jobmonitor.JobServiceMonitorClient) ([]*jobmonitor.Queue, error)); ok { diff --git a/src/testing/pkg/jobmonitor/redis_client.go b/src/testing/pkg/jobmonitor/redis_client.go index bf1c5c4e4..5a7f91837 100644 --- a/src/testing/pkg/jobmonitor/redis_client.go +++ b/src/testing/pkg/jobmonitor/redis_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobmonitor @@ -17,6 +17,10 @@ type RedisClient struct { func (_m *RedisClient) AllJobTypes(ctx context.Context) ([]string, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for AllJobTypes") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { @@ -43,6 +47,10 @@ func (_m *RedisClient) AllJobTypes(ctx context.Context) ([]string, error) { func (_m *RedisClient) PauseJob(ctx context.Context, jobName string) error { ret := _m.Called(ctx, jobName) + if len(ret) == 0 { + panic("no return value specified for PauseJob") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, jobName) @@ -57,6 +65,10 @@ func (_m *RedisClient) PauseJob(ctx context.Context, jobName string) error { func (_m *RedisClient) StopPendingJobs(ctx context.Context, jobType string) ([]string, error) { ret := _m.Called(ctx, jobType) + if len(ret) == 0 { + panic("no return value specified for StopPendingJobs") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]string, error)); ok { @@ -83,6 +95,10 @@ func (_m *RedisClient) StopPendingJobs(ctx context.Context, jobType string) ([]s func (_m *RedisClient) UnpauseJob(ctx context.Context, jobName string) error { ret := _m.Called(ctx, jobName) + if len(ret) == 0 { + panic("no return value specified for UnpauseJob") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, jobName) diff --git a/src/testing/pkg/jobmonitor/worker_manager.go b/src/testing/pkg/jobmonitor/worker_manager.go index 35563d24b..d6f136f2a 100644 --- a/src/testing/pkg/jobmonitor/worker_manager.go +++ b/src/testing/pkg/jobmonitor/worker_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package jobmonitor @@ -18,6 +18,10 @@ type WorkerManager struct { func (_m *WorkerManager) List(ctx context.Context, monitClient jobmonitor.JobServiceMonitorClient, poolID string) ([]*jobmonitor.Worker, error) { ret := _m.Called(ctx, monitClient, poolID) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*jobmonitor.Worker var r1 error if rf, ok := ret.Get(0).(func(context.Context, jobmonitor.JobServiceMonitorClient, string) ([]*jobmonitor.Worker, error)); ok { diff --git a/src/testing/pkg/label/dao/dao.go b/src/testing/pkg/label/dao/dao.go index 71b64de51..63cb4525b 100644 --- a/src/testing/pkg/label/dao/dao.go +++ b/src/testing/pkg/label/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, label *model.Label) (int64, error) { ret := _m.Called(ctx, label) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Label) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, label *model.Label) (int64, error) { func (_m *DAO) CreateReference(ctx context.Context, reference *model.Reference) (int64, error) { ret := _m.Called(ctx, reference) + if len(ret) == 0 { + panic("no return value specified for CreateReference") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Reference) (int64, error)); ok { @@ -93,6 +105,10 @@ func (_m *DAO) CreateReference(ctx context.Context, reference *model.Reference) func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -107,6 +123,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) DeleteReference(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteReference") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -121,6 +141,10 @@ func (_m *DAO) DeleteReference(ctx context.Context, id int64) error { func (_m *DAO) DeleteReferences(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for DeleteReferences") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -145,6 +169,10 @@ func (_m *DAO) DeleteReferences(ctx context.Context, query *q.Query) (int64, err func (_m *DAO) Get(ctx context.Context, id int64) (*model.Label, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Label, error)); ok { @@ -171,6 +199,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.Label, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Label, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Label, error)); ok { @@ -197,6 +229,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Label, error) func (_m *DAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) { ret := _m.Called(ctx, artifactID) + if len(ret) == 0 { + panic("no return value specified for ListByArtifact") + } + var r0 []*model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*model.Label, error)); ok { @@ -223,6 +259,10 @@ func (_m *DAO) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.L func (_m *DAO) Update(ctx context.Context, label *model.Label) error { ret := _m.Called(ctx, label) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Label) error); ok { r0 = rf(ctx, label) diff --git a/src/testing/pkg/label/manager.go b/src/testing/pkg/label/manager.go index 9fbd4727d..73c722944 100644 --- a/src/testing/pkg/label/manager.go +++ b/src/testing/pkg/label/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package label @@ -21,6 +21,10 @@ type Manager struct { func (_m *Manager) AddTo(ctx context.Context, labelID int64, artifactID int64) error { ret := _m.Called(ctx, labelID, artifactID) + if len(ret) == 0 { + panic("no return value specified for AddTo") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok { r0 = rf(ctx, labelID, artifactID) @@ -35,6 +39,10 @@ func (_m *Manager) AddTo(ctx context.Context, labelID int64, artifactID int64) e func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -59,6 +67,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *model.Label) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Label) (int64, error)); ok { @@ -83,6 +95,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *model.Label) (int64, error) func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -97,6 +113,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.Label, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Label, error)); ok { @@ -123,6 +143,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.Label, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Label, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Label, error)); ok { @@ -149,6 +173,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Label, er func (_m *Manager) ListByArtifact(ctx context.Context, artifactID int64) ([]*model.Label, error) { ret := _m.Called(ctx, artifactID) + if len(ret) == 0 { + panic("no return value specified for ListByArtifact") + } + var r0 []*model.Label var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]*model.Label, error)); ok { @@ -175,6 +203,10 @@ func (_m *Manager) ListByArtifact(ctx context.Context, artifactID int64) ([]*mod func (_m *Manager) RemoveAllFrom(ctx context.Context, artifactID int64) error { ret := _m.Called(ctx, artifactID) + if len(ret) == 0 { + panic("no return value specified for RemoveAllFrom") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, artifactID) @@ -189,6 +221,10 @@ func (_m *Manager) RemoveAllFrom(ctx context.Context, artifactID int64) error { func (_m *Manager) RemoveFrom(ctx context.Context, labelID int64, artifactID int64) error { ret := _m.Called(ctx, labelID, artifactID) + if len(ret) == 0 { + panic("no return value specified for RemoveFrom") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int64) error); ok { r0 = rf(ctx, labelID, artifactID) @@ -203,6 +239,10 @@ func (_m *Manager) RemoveFrom(ctx context.Context, labelID int64, artifactID int func (_m *Manager) RemoveFromAllArtifacts(ctx context.Context, labelID int64) error { ret := _m.Called(ctx, labelID) + if len(ret) == 0 { + panic("no return value specified for RemoveFromAllArtifacts") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, labelID) @@ -217,6 +257,10 @@ func (_m *Manager) RemoveFromAllArtifacts(ctx context.Context, labelID int64) er func (_m *Manager) Update(ctx context.Context, _a1 *model.Label) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Label) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/pkg/ldap/manager.go b/src/testing/pkg/ldap/manager.go index 4303c79bf..de682ae5d 100644 --- a/src/testing/pkg/ldap/manager.go +++ b/src/testing/pkg/ldap/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package ldap @@ -22,6 +22,10 @@ type Manager struct { func (_m *Manager) ImportUser(ctx context.Context, sess *ldap.Session, ldapImportUsers []string) ([]model.FailedImportUser, error) { ret := _m.Called(ctx, sess, ldapImportUsers) + if len(ret) == 0 { + panic("no return value specified for ImportUser") + } + var r0 []model.FailedImportUser var r1 error if rf, ok := ret.Get(0).(func(context.Context, *ldap.Session, []string) ([]model.FailedImportUser, error)); ok { @@ -48,6 +52,10 @@ func (_m *Manager) ImportUser(ctx context.Context, sess *ldap.Session, ldapImpor func (_m *Manager) Ping(ctx context.Context, cfg models.LdapConf) (bool, error) { ret := _m.Called(ctx, cfg) + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, models.LdapConf) (bool, error)); ok { @@ -72,6 +80,10 @@ func (_m *Manager) Ping(ctx context.Context, cfg models.LdapConf) (bool, error) func (_m *Manager) SearchGroup(ctx context.Context, sess *ldap.Session, groupName string, groupDN string) ([]model.Group, error) { ret := _m.Called(ctx, sess, groupName, groupDN) + if len(ret) == 0 { + panic("no return value specified for SearchGroup") + } + var r0 []model.Group var r1 error if rf, ok := ret.Get(0).(func(context.Context, *ldap.Session, string, string) ([]model.Group, error)); ok { @@ -98,6 +110,10 @@ func (_m *Manager) SearchGroup(ctx context.Context, sess *ldap.Session, groupNam func (_m *Manager) SearchUser(ctx context.Context, sess *ldap.Session, username string) ([]model.User, error) { ret := _m.Called(ctx, sess, username) + if len(ret) == 0 { + panic("no return value specified for SearchUser") + } + var r0 []model.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, *ldap.Session, string) ([]model.User, error)); ok { diff --git a/src/testing/pkg/member/fake_member_manager.go b/src/testing/pkg/member/fake_member_manager.go index af75d1746..1517a3784 100644 --- a/src/testing/pkg/member/fake_member_manager.go +++ b/src/testing/pkg/member/fake_member_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package member @@ -21,6 +21,10 @@ type Manager struct { func (_m *Manager) AddProjectMember(ctx context.Context, _a1 models.Member) (int, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for AddProjectMember") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, models.Member) (int, error)); ok { @@ -45,6 +49,10 @@ func (_m *Manager) AddProjectMember(ctx context.Context, _a1 models.Member) (int func (_m *Manager) Delete(ctx context.Context, projectID int64, memberID int) error { ret := _m.Called(ctx, projectID, memberID) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int) error); ok { r0 = rf(ctx, projectID, memberID) @@ -59,6 +67,10 @@ func (_m *Manager) Delete(ctx context.Context, projectID int64, memberID int) er func (_m *Manager) DeleteMemberByProjectID(ctx context.Context, projectID int64) error { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for DeleteMemberByProjectID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, projectID) @@ -73,6 +85,10 @@ func (_m *Manager) DeleteMemberByProjectID(ctx context.Context, projectID int64) func (_m *Manager) DeleteMemberByUserID(ctx context.Context, uid int) error { ret := _m.Called(ctx, uid) + if len(ret) == 0 { + panic("no return value specified for DeleteMemberByUserID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, uid) @@ -87,6 +103,10 @@ func (_m *Manager) DeleteMemberByUserID(ctx context.Context, uid int) error { func (_m *Manager) Get(ctx context.Context, projectID int64, memberID int) (*models.Member, error) { ret := _m.Called(ctx, projectID, memberID) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Member var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int) (*models.Member, error)); ok { @@ -120,6 +140,10 @@ func (_m *Manager) GetTotalOfProjectMembers(ctx context.Context, projectID int64 _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetTotalOfProjectMembers") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query, ...int) (int, error)); ok { @@ -144,6 +168,10 @@ func (_m *Manager) GetTotalOfProjectMembers(ctx context.Context, projectID int64 func (_m *Manager) List(ctx context.Context, queryMember models.Member, query *q.Query) ([]*models.Member, error) { ret := _m.Called(ctx, queryMember, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Member var r1 error if rf, ok := ret.Get(0).(func(context.Context, models.Member, *q.Query) ([]*models.Member, error)); ok { @@ -170,6 +198,10 @@ func (_m *Manager) List(ctx context.Context, queryMember models.Member, query *q func (_m *Manager) ListRoles(ctx context.Context, user *models.User, projectID int64) ([]int, error) { ret := _m.Called(ctx, user, projectID) + if len(ret) == 0 { + panic("no return value specified for ListRoles") + } + var r0 []int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.User, int64) ([]int, error)); ok { @@ -196,6 +228,10 @@ func (_m *Manager) ListRoles(ctx context.Context, user *models.User, projectID i func (_m *Manager) SearchMemberByName(ctx context.Context, projectID int64, entityName string) ([]*models.Member, error) { ret := _m.Called(ctx, projectID, entityName) + if len(ret) == 0 { + panic("no return value specified for SearchMemberByName") + } + var r0 []*models.Member var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) ([]*models.Member, error)); ok { @@ -222,6 +258,10 @@ func (_m *Manager) SearchMemberByName(ctx context.Context, projectID int64, enti func (_m *Manager) UpdateRole(ctx context.Context, projectID int64, pmID int, role int) error { ret := _m.Called(ctx, projectID, pmID, role) + if len(ret) == 0 { + panic("no return value specified for UpdateRole") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, int, int) error); ok { r0 = rf(ctx, projectID, pmID, role) diff --git a/src/testing/pkg/notification/policy/dao/dao.go b/src/testing/pkg/notification/policy/dao/dao.go index 55d9e9ba4..83181b5a4 100644 --- a/src/testing/pkg/notification/policy/dao/dao.go +++ b/src/testing/pkg/notification/policy/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, n *model.Policy) (int64, error) { ret := _m.Called(ctx, n) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, n *model.Policy) (int64, error) { func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -109,6 +125,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.Policy, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -135,6 +155,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Policy, error func (_m *DAO) Update(ctx context.Context, n *model.Policy) error { ret := _m.Called(ctx, n) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) error); ok { r0 = rf(ctx, n) diff --git a/src/testing/pkg/notification/policy/manager.go b/src/testing/pkg/notification/policy/manager.go index 716d91b44..b68aaecb5 100644 --- a/src/testing/pkg/notification/policy/manager.go +++ b/src/testing/pkg/notification/policy/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package policy @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *model.Policy) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *model.Policy) (int64, error) func (_m *Manager) Delete(ctx context.Context, policyID int64) error { ret := _m.Called(ctx, policyID) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, policyID) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, policyID int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -108,6 +124,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.Policy, error) { func (_m *Manager) GetRelatedPolices(ctx context.Context, projectID int64, eventType string) ([]*model.Policy, error) { ret := _m.Called(ctx, projectID, eventType) + if len(ret) == 0 { + panic("no return value specified for GetRelatedPolices") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) ([]*model.Policy, error)); ok { @@ -134,6 +154,10 @@ func (_m *Manager) GetRelatedPolices(ctx context.Context, projectID int64, event func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -160,6 +184,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Policy, e func (_m *Manager) Update(ctx context.Context, _a1 *model.Policy) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/pkg/oidc/dao/meta_dao.go b/src/testing/pkg/oidc/dao/meta_dao.go index d66a416e0..c773133ef 100644 --- a/src/testing/pkg/oidc/dao/meta_dao.go +++ b/src/testing/pkg/oidc/dao/meta_dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type MetaDAO struct { func (_m *MetaDAO) Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error) { ret := _m.Called(ctx, oidcUser) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.OIDCUser) (int, error)); ok { @@ -45,6 +49,10 @@ func (_m *MetaDAO) Create(ctx context.Context, oidcUser *models.OIDCUser) (int, func (_m *MetaDAO) DeleteByUserID(ctx context.Context, uid int) error { ret := _m.Called(ctx, uid) + if len(ret) == 0 { + panic("no return value specified for DeleteByUserID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, uid) @@ -59,6 +67,10 @@ func (_m *MetaDAO) DeleteByUserID(ctx context.Context, uid int) error { func (_m *MetaDAO) GetByUsername(ctx context.Context, username string) (*models.OIDCUser, error) { ret := _m.Called(ctx, username) + if len(ret) == 0 { + panic("no return value specified for GetByUsername") + } + var r0 *models.OIDCUser var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*models.OIDCUser, error)); ok { @@ -85,6 +97,10 @@ func (_m *MetaDAO) GetByUsername(ctx context.Context, username string) (*models. func (_m *MetaDAO) List(ctx context.Context, query *q.Query) ([]*models.OIDCUser, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.OIDCUser var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.OIDCUser, error)); ok { @@ -118,6 +134,10 @@ func (_m *MetaDAO) Update(ctx context.Context, oidcUser *models.OIDCUser, props _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.OIDCUser, ...string) error); ok { r0 = rf(ctx, oidcUser, props...) diff --git a/src/testing/pkg/oidc/meta_manager.go b/src/testing/pkg/oidc/meta_manager.go index 676a33623..c9a688a2e 100644 --- a/src/testing/pkg/oidc/meta_manager.go +++ b/src/testing/pkg/oidc/meta_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package oidc @@ -18,6 +18,10 @@ type MetaManager struct { func (_m *MetaManager) Create(ctx context.Context, oidcUser *models.OIDCUser) (int, error) { ret := _m.Called(ctx, oidcUser) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.OIDCUser) (int, error)); ok { @@ -42,6 +46,10 @@ func (_m *MetaManager) Create(ctx context.Context, oidcUser *models.OIDCUser) (i func (_m *MetaManager) DeleteByUserID(ctx context.Context, uid int) error { ret := _m.Called(ctx, uid) + if len(ret) == 0 { + panic("no return value specified for DeleteByUserID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, uid) @@ -56,6 +64,10 @@ func (_m *MetaManager) DeleteByUserID(ctx context.Context, uid int) error { func (_m *MetaManager) GetBySubIss(ctx context.Context, sub string, iss string) (*models.OIDCUser, error) { ret := _m.Called(ctx, sub, iss) + if len(ret) == 0 { + panic("no return value specified for GetBySubIss") + } + var r0 *models.OIDCUser var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (*models.OIDCUser, error)); ok { @@ -82,6 +94,10 @@ func (_m *MetaManager) GetBySubIss(ctx context.Context, sub string, iss string) func (_m *MetaManager) GetByUserID(ctx context.Context, uid int) (*models.OIDCUser, error) { ret := _m.Called(ctx, uid) + if len(ret) == 0 { + panic("no return value specified for GetByUserID") + } + var r0 *models.OIDCUser var r1 error if rf, ok := ret.Get(0).(func(context.Context, int) (*models.OIDCUser, error)); ok { @@ -108,6 +124,10 @@ func (_m *MetaManager) GetByUserID(ctx context.Context, uid int) (*models.OIDCUs func (_m *MetaManager) SetCliSecretByUserID(ctx context.Context, uid int, secret string) error { ret := _m.Called(ctx, uid, secret) + if len(ret) == 0 { + panic("no return value specified for SetCliSecretByUserID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok { r0 = rf(ctx, uid, secret) @@ -129,6 +149,10 @@ func (_m *MetaManager) Update(ctx context.Context, oidcUser *models.OIDCUser, co _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.OIDCUser, ...string) error); ok { r0 = rf(ctx, oidcUser, cols...) diff --git a/src/testing/pkg/project/manager.go b/src/testing/pkg/project/manager.go index c29bb1c9f..614a5e067 100644 --- a/src/testing/pkg/project/manager.go +++ b/src/testing/pkg/project/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package project @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *models.Project) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.Project) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *models.Project) (int64, erro func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, idOrName interface{}) (*models.Project, error) { ret := _m.Called(ctx, idOrName) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Project var r1 error if rf, ok := ret.Get(0).(func(context.Context, interface{}) (*models.Project, error)); ok { @@ -108,6 +124,10 @@ func (_m *Manager) Get(ctx context.Context, idOrName interface{}) (*models.Proje func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Project, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Project var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.Project, error)); ok { @@ -134,6 +154,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Project, func (_m *Manager) ListAdminRolesOfUser(ctx context.Context, userID int) ([]models.Member, error) { ret := _m.Called(ctx, userID) + if len(ret) == 0 { + panic("no return value specified for ListAdminRolesOfUser") + } + var r0 []models.Member var r1 error if rf, ok := ret.Get(0).(func(context.Context, int) ([]models.Member, error)); ok { @@ -167,6 +191,10 @@ func (_m *Manager) ListRoles(ctx context.Context, projectID int64, userID int, g _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ListRoles") + } + var r0 []int var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, int, ...int) ([]int, error)); ok { diff --git a/src/testing/pkg/project/metadata/manager.go b/src/testing/pkg/project/metadata/manager.go index 3a6e071c0..7135b3be2 100644 --- a/src/testing/pkg/project/metadata/manager.go +++ b/src/testing/pkg/project/metadata/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package metadata @@ -19,6 +19,10 @@ type Manager struct { func (_m *Manager) Add(ctx context.Context, projectID int64, meta map[string]string) error { ret := _m.Called(ctx, projectID, meta) + if len(ret) == 0 { + panic("no return value specified for Add") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, map[string]string) error); ok { r0 = rf(ctx, projectID, meta) @@ -40,6 +44,10 @@ func (_m *Manager) Delete(ctx context.Context, projectID int64, meta ...string) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, ...string) error); ok { r0 = rf(ctx, projectID, meta...) @@ -61,6 +69,10 @@ func (_m *Manager) Get(ctx context.Context, projectID int64, meta ...string) (ma _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 map[string]string var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, ...string) (map[string]string, error)); ok { @@ -87,6 +99,10 @@ func (_m *Manager) Get(ctx context.Context, projectID int64, meta ...string) (ma func (_m *Manager) List(ctx context.Context, name string, value string) ([]*models.ProjectMetadata, error) { ret := _m.Called(ctx, name, value) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.ProjectMetadata var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]*models.ProjectMetadata, error)); ok { @@ -113,6 +129,10 @@ func (_m *Manager) List(ctx context.Context, name string, value string) ([]*mode func (_m *Manager) Update(ctx context.Context, projectID int64, meta map[string]string) error { ret := _m.Called(ctx, projectID, meta) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, map[string]string) error); ok { r0 = rf(ctx, projectID, meta) diff --git a/src/testing/pkg/queuestatus/manager.go b/src/testing/pkg/queuestatus/manager.go index 836631dfe..924bc4ca8 100644 --- a/src/testing/pkg/queuestatus/manager.go +++ b/src/testing/pkg/queuestatus/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package queuestatus @@ -18,6 +18,10 @@ type Manager struct { func (_m *Manager) AllJobTypeStatus(ctx context.Context) (map[string]bool, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for AllJobTypeStatus") + } + var r0 map[string]bool var r1 error if rf, ok := ret.Get(0).(func(context.Context) (map[string]bool, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) AllJobTypeStatus(ctx context.Context) (map[string]bool, error func (_m *Manager) CreateOrUpdate(ctx context.Context, status *model.JobQueueStatus) (int64, error) { ret := _m.Called(ctx, status) + if len(ret) == 0 { + panic("no return value specified for CreateOrUpdate") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.JobQueueStatus) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) CreateOrUpdate(ctx context.Context, status *model.JobQueueSta func (_m *Manager) Get(ctx context.Context, jobType string) (*model.JobQueueStatus, error) { ret := _m.Called(ctx, jobType) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.JobQueueStatus var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*model.JobQueueStatus, error)); ok { @@ -94,6 +106,10 @@ func (_m *Manager) Get(ctx context.Context, jobType string) (*model.JobQueueStat func (_m *Manager) List(ctx context.Context) ([]*model.JobQueueStatus, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.JobQueueStatus var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]*model.JobQueueStatus, error)); ok { @@ -120,6 +136,10 @@ func (_m *Manager) List(ctx context.Context) ([]*model.JobQueueStatus, error) { func (_m *Manager) UpdateStatus(ctx context.Context, jobType string, paused bool) error { ret := _m.Called(ctx, jobType, paused) + if len(ret) == 0 { + panic("no return value specified for UpdateStatus") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, bool) error); ok { r0 = rf(ctx, jobType, paused) diff --git a/src/testing/pkg/quota/driver/driver.go b/src/testing/pkg/quota/driver/driver.go index 377caf2b0..2ef8b7c37 100644 --- a/src/testing/pkg/quota/driver/driver.go +++ b/src/testing/pkg/quota/driver/driver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package driver @@ -20,6 +20,10 @@ type Driver struct { func (_m *Driver) CalculateUsage(ctx context.Context, key string) (types.ResourceList, error) { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for CalculateUsage") + } + var r0 types.ResourceList var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (types.ResourceList, error)); ok { @@ -46,6 +50,10 @@ func (_m *Driver) CalculateUsage(ctx context.Context, key string) (types.Resourc func (_m *Driver) Enabled(ctx context.Context, key string) (bool, error) { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Enabled") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (bool, error)); ok { @@ -70,6 +78,10 @@ func (_m *Driver) Enabled(ctx context.Context, key string) (bool, error) { func (_m *Driver) HardLimits(ctx context.Context) types.ResourceList { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for HardLimits") + } + var r0 types.ResourceList if rf, ok := ret.Get(0).(func(context.Context) types.ResourceList); ok { r0 = rf(ctx) @@ -86,6 +98,10 @@ func (_m *Driver) HardLimits(ctx context.Context) types.ResourceList { func (_m *Driver) Load(ctx context.Context, key string) (driver.RefObject, error) { ret := _m.Called(ctx, key) + if len(ret) == 0 { + panic("no return value specified for Load") + } + var r0 driver.RefObject var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (driver.RefObject, error)); ok { @@ -112,6 +128,10 @@ func (_m *Driver) Load(ctx context.Context, key string) (driver.RefObject, error func (_m *Driver) Validate(hardLimits types.ResourceList) error { ret := _m.Called(hardLimits) + if len(ret) == 0 { + panic("no return value specified for Validate") + } + var r0 error if rf, ok := ret.Get(0).(func(types.ResourceList) error); ok { r0 = rf(hardLimits) diff --git a/src/testing/pkg/quota/manager.go b/src/testing/pkg/quota/manager.go index b5feaaec6..4d570f3ed 100644 --- a/src/testing/pkg/quota/manager.go +++ b/src/testing/pkg/quota/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package quota @@ -22,6 +22,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -53,6 +57,10 @@ func (_m *Manager) Create(ctx context.Context, reference string, referenceID str _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, types.ResourceList, ...types.ResourceList) (int64, error)); ok { @@ -77,6 +85,10 @@ func (_m *Manager) Create(ctx context.Context, reference string, referenceID str func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -91,6 +103,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*models.Quota, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*models.Quota, error)); ok { @@ -117,6 +133,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*models.Quota, error) { func (_m *Manager) GetByRef(ctx context.Context, reference string, referenceID string) (*models.Quota, error) { ret := _m.Called(ctx, reference, referenceID) + if len(ret) == 0 { + panic("no return value specified for GetByRef") + } + var r0 *models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (*models.Quota, error)); ok { @@ -143,6 +163,10 @@ func (_m *Manager) GetByRef(ctx context.Context, reference string, referenceID s func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Quota, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.Quota var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.Quota, error)); ok { @@ -169,6 +193,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*models.Quota, e func (_m *Manager) Update(ctx context.Context, _a1 *models.Quota) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.Quota) error); ok { r0 = rf(ctx, _a1) diff --git a/src/testing/pkg/rbac/dao/dao.go b/src/testing/pkg/rbac/dao/dao.go index 7f2138499..91a2c351c 100644 --- a/src/testing/pkg/rbac/dao/dao.go +++ b/src/testing/pkg/rbac/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) CreatePermission(ctx context.Context, rp *model.RolePermission) (int64, error) { ret := _m.Called(ctx, rp) + if len(ret) == 0 { + panic("no return value specified for CreatePermission") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.RolePermission) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) CreatePermission(ctx context.Context, rp *model.RolePermission) ( func (_m *DAO) CreateRbacPolicy(ctx context.Context, pp *model.PermissionPolicy) (int64, error) { ret := _m.Called(ctx, pp) + if len(ret) == 0 { + panic("no return value specified for CreateRbacPolicy") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.PermissionPolicy) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) CreateRbacPolicy(ctx context.Context, pp *model.PermissionPolicy) func (_m *DAO) DeletePermission(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeletePermission") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) DeletePermission(ctx context.Context, id int64) error { func (_m *DAO) DeletePermissionsByRole(ctx context.Context, roleType string, roleID int64) error { ret := _m.Called(ctx, roleType, roleID) + if len(ret) == 0 { + panic("no return value specified for DeletePermissionsByRole") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, roleType, roleID) @@ -97,6 +113,10 @@ func (_m *DAO) DeletePermissionsByRole(ctx context.Context, roleType string, rol func (_m *DAO) DeleteRbacPolicy(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteRbacPolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -111,6 +131,10 @@ func (_m *DAO) DeleteRbacPolicy(ctx context.Context, id int64) error { func (_m *DAO) GetPermissionsByRole(ctx context.Context, roleType string, roleID int64) ([]*model.UniversalRolePermission, error) { ret := _m.Called(ctx, roleType, roleID) + if len(ret) == 0 { + panic("no return value specified for GetPermissionsByRole") + } + var r0 []*model.UniversalRolePermission var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) ([]*model.UniversalRolePermission, error)); ok { @@ -137,6 +161,10 @@ func (_m *DAO) GetPermissionsByRole(ctx context.Context, roleType string, roleID func (_m *DAO) ListPermissions(ctx context.Context, query *q.Query) ([]*model.RolePermission, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListPermissions") + } + var r0 []*model.RolePermission var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.RolePermission, error)); ok { @@ -163,6 +191,10 @@ func (_m *DAO) ListPermissions(ctx context.Context, query *q.Query) ([]*model.Ro func (_m *DAO) ListRbacPolicies(ctx context.Context, query *q.Query) ([]*model.PermissionPolicy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListRbacPolicies") + } + var r0 []*model.PermissionPolicy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.PermissionPolicy, error)); ok { diff --git a/src/testing/pkg/rbac/manager.go b/src/testing/pkg/rbac/manager.go index 3de52e1d4..0cc460753 100644 --- a/src/testing/pkg/rbac/manager.go +++ b/src/testing/pkg/rbac/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package rbac @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) CreatePermission(ctx context.Context, rp *model.RolePermission) (int64, error) { ret := _m.Called(ctx, rp) + if len(ret) == 0 { + panic("no return value specified for CreatePermission") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.RolePermission) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) CreatePermission(ctx context.Context, rp *model.RolePermissio func (_m *Manager) CreateRbacPolicy(ctx context.Context, pp *model.PermissionPolicy) (int64, error) { ret := _m.Called(ctx, pp) + if len(ret) == 0 { + panic("no return value specified for CreateRbacPolicy") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.PermissionPolicy) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) CreateRbacPolicy(ctx context.Context, pp *model.PermissionPol func (_m *Manager) DeletePermission(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeletePermission") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) DeletePermission(ctx context.Context, id int64) error { func (_m *Manager) DeletePermissionsByRole(ctx context.Context, roleType string, roleID int64) error { ret := _m.Called(ctx, roleType, roleID) + if len(ret) == 0 { + panic("no return value specified for DeletePermissionsByRole") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, roleType, roleID) @@ -96,6 +112,10 @@ func (_m *Manager) DeletePermissionsByRole(ctx context.Context, roleType string, func (_m *Manager) DeleteRbacPolicy(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteRbacPolicy") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -110,6 +130,10 @@ func (_m *Manager) DeleteRbacPolicy(ctx context.Context, id int64) error { func (_m *Manager) GetPermissionsByRole(ctx context.Context, roleType string, roleID int64) ([]*model.UniversalRolePermission, error) { ret := _m.Called(ctx, roleType, roleID) + if len(ret) == 0 { + panic("no return value specified for GetPermissionsByRole") + } + var r0 []*model.UniversalRolePermission var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) ([]*model.UniversalRolePermission, error)); ok { @@ -136,6 +160,10 @@ func (_m *Manager) GetPermissionsByRole(ctx context.Context, roleType string, ro func (_m *Manager) ListPermissions(ctx context.Context, query *q.Query) ([]*model.RolePermission, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListPermissions") + } + var r0 []*model.RolePermission var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.RolePermission, error)); ok { @@ -162,6 +190,10 @@ func (_m *Manager) ListPermissions(ctx context.Context, query *q.Query) ([]*mode func (_m *Manager) ListRbacPolicies(ctx context.Context, query *q.Query) ([]*model.PermissionPolicy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListRbacPolicies") + } + var r0 []*model.PermissionPolicy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.PermissionPolicy, error)); ok { diff --git a/src/testing/pkg/reg/adapter/adapter.go b/src/testing/pkg/reg/adapter/adapter.go index 6f8ad39b5..a10a0f2ff 100644 --- a/src/testing/pkg/reg/adapter/adapter.go +++ b/src/testing/pkg/reg/adapter/adapter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package adapter @@ -16,6 +16,10 @@ type Adapter struct { func (_m *Adapter) HealthCheck() (string, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthCheck") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func() (string, error)); ok { @@ -40,6 +44,10 @@ func (_m *Adapter) HealthCheck() (string, error) { func (_m *Adapter) Info() (*model.RegistryInfo, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Info") + } + var r0 *model.RegistryInfo var r1 error if rf, ok := ret.Get(0).(func() (*model.RegistryInfo, error)); ok { @@ -66,6 +74,10 @@ func (_m *Adapter) Info() (*model.RegistryInfo, error) { func (_m *Adapter) PrepareForPush(_a0 []*model.Resource) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for PrepareForPush") + } + var r0 error if rf, ok := ret.Get(0).(func([]*model.Resource) error); ok { r0 = rf(_a0) diff --git a/src/testing/pkg/reg/dao/dao.go b/src/testing/pkg/reg/dao/dao.go index 0f11dd37f..23ae0ecc8 100644 --- a/src/testing/pkg/reg/dao/dao.go +++ b/src/testing/pkg/reg/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -20,6 +20,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, registry *dao.Registry) (int64, error) { ret := _m.Called(ctx, registry) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Registry) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *DAO) Create(ctx context.Context, registry *dao.Registry) (int64, error func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*dao.Registry, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *dao.Registry var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*dao.Registry, error)); ok { @@ -108,6 +124,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*dao.Registry, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*dao.Registry, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*dao.Registry var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*dao.Registry, error)); ok { @@ -141,6 +161,10 @@ func (_m *DAO) Update(ctx context.Context, registry *dao.Registry, props ...stri _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *dao.Registry, ...string) error); ok { r0 = rf(ctx, registry, props...) diff --git a/src/testing/pkg/reg/manager.go b/src/testing/pkg/reg/manager.go index 291af3927..c25e542d1 100644 --- a/src/testing/pkg/reg/manager.go +++ b/src/testing/pkg/reg/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package manager @@ -23,6 +23,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -47,6 +51,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, registry *model.Registry) (int64, error) { ret := _m.Called(ctx, registry) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Registry) (int64, error)); ok { @@ -71,6 +79,10 @@ func (_m *Manager) Create(ctx context.Context, registry *model.Registry) (int64, func (_m *Manager) CreateAdapter(ctx context.Context, registry *model.Registry) (adapter.Adapter, error) { ret := _m.Called(ctx, registry) + if len(ret) == 0 { + panic("no return value specified for CreateAdapter") + } + var r0 adapter.Adapter var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Registry) (adapter.Adapter, error)); ok { @@ -97,6 +109,10 @@ func (_m *Manager) CreateAdapter(ctx context.Context, registry *model.Registry) func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -111,6 +127,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.Registry, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Registry var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Registry, error)); ok { @@ -137,6 +157,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.Registry, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Registry, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Registry var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Registry, error)); ok { @@ -163,6 +187,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Registry, func (_m *Manager) ListRegistryProviderInfos(ctx context.Context) (map[string]*model.AdapterPattern, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ListRegistryProviderInfos") + } + var r0 map[string]*model.AdapterPattern var r1 error if rf, ok := ret.Get(0).(func(context.Context) (map[string]*model.AdapterPattern, error)); ok { @@ -189,6 +217,10 @@ func (_m *Manager) ListRegistryProviderInfos(ctx context.Context) (map[string]*m func (_m *Manager) ListRegistryProviderTypes(ctx context.Context) ([]string, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ListRegistryProviderTypes") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]string, error)); ok { @@ -222,6 +254,10 @@ func (_m *Manager) Update(ctx context.Context, registry *model.Registry, props . _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Registry, ...string) error); ok { r0 = rf(ctx, registry, props...) diff --git a/src/testing/pkg/registry/fake_registry_client.go b/src/testing/pkg/registry/fake_registry_client.go index adb9c3446..72a6decf2 100644 --- a/src/testing/pkg/registry/fake_registry_client.go +++ b/src/testing/pkg/registry/fake_registry_client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package registry @@ -21,6 +21,10 @@ type Client struct { func (_m *Client) BlobExist(repository string, digest string) (bool, error) { ret := _m.Called(repository, digest) + if len(ret) == 0 { + panic("no return value specified for BlobExist") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(string, string) (bool, error)); ok { @@ -45,6 +49,10 @@ func (_m *Client) BlobExist(repository string, digest string) (bool, error) { func (_m *Client) Catalog() ([]string, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Catalog") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func() ([]string, error)); ok { @@ -71,6 +79,10 @@ func (_m *Client) Catalog() ([]string, error) { func (_m *Client) Copy(srcRepository string, srcReference string, dstRepository string, dstReference string, override bool) error { ret := _m.Called(srcRepository, srcReference, dstRepository, dstReference, override) + if len(ret) == 0 { + panic("no return value specified for Copy") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string, string, string, bool) error); ok { r0 = rf(srcRepository, srcReference, dstRepository, dstReference, override) @@ -85,6 +97,10 @@ func (_m *Client) Copy(srcRepository string, srcReference string, dstRepository func (_m *Client) DeleteBlob(repository string, digest string) error { ret := _m.Called(repository, digest) + if len(ret) == 0 { + panic("no return value specified for DeleteBlob") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(repository, digest) @@ -99,6 +115,10 @@ func (_m *Client) DeleteBlob(repository string, digest string) error { func (_m *Client) DeleteManifest(repository string, reference string) error { ret := _m.Called(repository, reference) + if len(ret) == 0 { + panic("no return value specified for DeleteManifest") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string) error); ok { r0 = rf(repository, reference) @@ -113,6 +133,10 @@ func (_m *Client) DeleteManifest(repository string, reference string) error { func (_m *Client) Do(req *http.Request) (*http.Response, error) { ret := _m.Called(req) + if len(ret) == 0 { + panic("no return value specified for Do") + } + var r0 *http.Response var r1 error if rf, ok := ret.Get(0).(func(*http.Request) (*http.Response, error)); ok { @@ -139,6 +163,10 @@ func (_m *Client) Do(req *http.Request) (*http.Response, error) { func (_m *Client) ListTags(repository string) ([]string, error) { ret := _m.Called(repository) + if len(ret) == 0 { + panic("no return value specified for ListTags") + } + var r0 []string var r1 error if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok { @@ -165,6 +193,10 @@ func (_m *Client) ListTags(repository string) ([]string, error) { func (_m *Client) ManifestExist(repository string, reference string) (bool, *distribution.Descriptor, error) { ret := _m.Called(repository, reference) + if len(ret) == 0 { + panic("no return value specified for ManifestExist") + } + var r0 bool var r1 *distribution.Descriptor var r2 error @@ -198,6 +230,10 @@ func (_m *Client) ManifestExist(repository string, reference string) (bool, *dis func (_m *Client) MountBlob(srcRepository string, digest string, dstRepository string) error { ret := _m.Called(srcRepository, digest, dstRepository) + if len(ret) == 0 { + panic("no return value specified for MountBlob") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string, string) error); ok { r0 = rf(srcRepository, digest, dstRepository) @@ -212,6 +248,10 @@ func (_m *Client) MountBlob(srcRepository string, digest string, dstRepository s func (_m *Client) Ping() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Ping") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -226,6 +266,10 @@ func (_m *Client) Ping() error { func (_m *Client) PullBlob(repository string, digest string) (int64, io.ReadCloser, error) { ret := _m.Called(repository, digest) + if len(ret) == 0 { + panic("no return value specified for PullBlob") + } + var r0 int64 var r1 io.ReadCloser var r2 error @@ -259,6 +303,10 @@ func (_m *Client) PullBlob(repository string, digest string) (int64, io.ReadClos func (_m *Client) PullBlobChunk(repository string, digest string, blobSize int64, start int64, end int64) (int64, io.ReadCloser, error) { ret := _m.Called(repository, digest, blobSize, start, end) + if len(ret) == 0 { + panic("no return value specified for PullBlobChunk") + } + var r0 int64 var r1 io.ReadCloser var r2 error @@ -299,6 +347,10 @@ func (_m *Client) PullManifest(repository string, reference string, acceptedMedi _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PullManifest") + } + var r0 distribution.Manifest var r1 string var r2 error @@ -332,6 +384,10 @@ func (_m *Client) PullManifest(repository string, reference string, acceptedMedi func (_m *Client) PushBlob(repository string, digest string, size int64, blob io.Reader) error { ret := _m.Called(repository, digest, size, blob) + if len(ret) == 0 { + panic("no return value specified for PushBlob") + } + var r0 error if rf, ok := ret.Get(0).(func(string, string, int64, io.Reader) error); ok { r0 = rf(repository, digest, size, blob) @@ -346,6 +402,10 @@ func (_m *Client) PushBlob(repository string, digest string, size int64, blob io func (_m *Client) PushBlobChunk(repository string, digest string, blobSize int64, chunk io.Reader, start int64, end int64, location string) (string, int64, error) { ret := _m.Called(repository, digest, blobSize, chunk, start, end, location) + if len(ret) == 0 { + panic("no return value specified for PushBlobChunk") + } + var r0 string var r1 int64 var r2 error @@ -377,6 +437,10 @@ func (_m *Client) PushBlobChunk(repository string, digest string, blobSize int64 func (_m *Client) PushManifest(repository string, reference string, mediaType string, payload []byte) (string, error) { ret := _m.Called(repository, reference, mediaType, payload) + if len(ret) == 0 { + panic("no return value specified for PushManifest") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, string, string, []byte) (string, error)); ok { diff --git a/src/testing/pkg/replication/dao/dao.go b/src/testing/pkg/replication/dao/dao.go index 0a0ed964b..29c9a2be4 100644 --- a/src/testing/pkg/replication/dao/dao.go +++ b/src/testing/pkg/replication/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, policy *model.Policy) (int64, error) { ret := _m.Called(ctx, policy) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, policy *model.Policy) (int64, error) func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -109,6 +125,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.Policy, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -142,6 +162,10 @@ func (_m *DAO) Update(ctx context.Context, policy *model.Policy, props ...string _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy, ...string) error); ok { r0 = rf(ctx, policy, props...) diff --git a/src/testing/pkg/replication/manager.go b/src/testing/pkg/replication/manager.go index 9289c4f8c..652331740 100644 --- a/src/testing/pkg/replication/manager.go +++ b/src/testing/pkg/replication/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package manager @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, policy *model.Policy) (int64, error) { ret := _m.Called(ctx, policy) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, policy *model.Policy) (int64, err func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.Policy, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Policy, error)); ok { @@ -108,6 +124,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.Policy, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Policy, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Policy var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Policy, error)); ok { @@ -141,6 +161,10 @@ func (_m *Manager) Update(ctx context.Context, policy *model.Policy, props ...st _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Policy, ...string) error); ok { r0 = rf(ctx, policy, props...) diff --git a/src/testing/pkg/repository/dao/dao.go b/src/testing/pkg/repository/dao/dao.go index 7e5563eed..59094a22c 100644 --- a/src/testing/pkg/repository/dao/dao.go +++ b/src/testing/pkg/repository/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) AddPullCount(ctx context.Context, id int64, count uint64) error { ret := _m.Called(ctx, id, count) + if len(ret) == 0 { + panic("no return value specified for AddPullCount") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, uint64) error); ok { r0 = rf(ctx, id, count) @@ -35,6 +39,10 @@ func (_m *DAO) AddPullCount(ctx context.Context, id int64, count uint64) error { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -59,6 +67,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, repository *model.RepoRecord) (int64, error) { ret := _m.Called(ctx, repository) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.RepoRecord) (int64, error)); ok { @@ -83,6 +95,10 @@ func (_m *DAO) Create(ctx context.Context, repository *model.RepoRecord) (int64, func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -97,6 +113,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*model.RepoRecord, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.RepoRecord, error)); ok { @@ -123,6 +143,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.RepoRecord, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.RepoRecord, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.RepoRecord, error)); ok { @@ -149,6 +173,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.RepoRecord, e func (_m *DAO) NonEmptyRepos(ctx context.Context) ([]*model.RepoRecord, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for NonEmptyRepos") + } + var r0 []*model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]*model.RepoRecord, error)); ok { @@ -182,6 +210,10 @@ func (_m *DAO) Update(ctx context.Context, repository *model.RepoRecord, props . _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.RepoRecord, ...string) error); ok { r0 = rf(ctx, repository, props...) diff --git a/src/testing/pkg/repository/manager.go b/src/testing/pkg/repository/manager.go index f0de300a5..db6df7bde 100644 --- a/src/testing/pkg/repository/manager.go +++ b/src/testing/pkg/repository/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package repository @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) AddPullCount(ctx context.Context, id int64, count uint64) error { ret := _m.Called(ctx, id, count) + if len(ret) == 0 { + panic("no return value specified for AddPullCount") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, uint64) error); ok { r0 = rf(ctx, id, count) @@ -34,6 +38,10 @@ func (_m *Manager) AddPullCount(ctx context.Context, id int64, count uint64) err func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -58,6 +66,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, _a1 *model.RepoRecord) (int64, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.RepoRecord) (int64, error)); ok { @@ -82,6 +94,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *model.RepoRecord) (int64, er func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -96,6 +112,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) Get(ctx context.Context, id int64) (*model.RepoRecord, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.RepoRecord, error)); ok { @@ -122,6 +142,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.RepoRecord, error) func (_m *Manager) GetByName(ctx context.Context, name string) (*model.RepoRecord, error) { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for GetByName") + } + var r0 *model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*model.RepoRecord, error)); ok { @@ -148,6 +172,10 @@ func (_m *Manager) GetByName(ctx context.Context, name string) (*model.RepoRecor func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.RepoRecord, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.RepoRecord, error)); ok { @@ -174,6 +202,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.RepoRecor func (_m *Manager) NonEmptyRepos(ctx context.Context) ([]*model.RepoRecord, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for NonEmptyRepos") + } + var r0 []*model.RepoRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]*model.RepoRecord, error)); ok { @@ -207,6 +239,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *model.RepoRecord, props ...s _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.RepoRecord, ...string) error); ok { r0 = rf(ctx, _a1, props...) diff --git a/src/testing/pkg/robot/dao/dao.go b/src/testing/pkg/robot/dao/dao.go index d3255265f..f1160128f 100644 --- a/src/testing/pkg/robot/dao/dao.go +++ b/src/testing/pkg/robot/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, r *model.Robot) (int64, error) { ret := _m.Called(ctx, r) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Robot) (int64, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, r *model.Robot) (int64, error) { func (_m *DAO) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -83,6 +95,10 @@ func (_m *DAO) Delete(ctx context.Context, id int64) error { func (_m *DAO) DeleteByProjectID(ctx context.Context, projectID int64) error { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for DeleteByProjectID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, projectID) @@ -97,6 +113,10 @@ func (_m *DAO) DeleteByProjectID(ctx context.Context, projectID int64) error { func (_m *DAO) Get(ctx context.Context, id int64) (*model.Robot, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Robot, error)); ok { @@ -123,6 +143,10 @@ func (_m *DAO) Get(ctx context.Context, id int64) (*model.Robot, error) { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Robot, error)); ok { @@ -156,6 +180,10 @@ func (_m *DAO) Update(ctx context.Context, r *model.Robot, props ...string) erro _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Robot, ...string) error); ok { r0 = rf(ctx, r, props...) diff --git a/src/testing/pkg/robot/manager.go b/src/testing/pkg/robot/manager.go index 56be7000c..12101d09e 100644 --- a/src/testing/pkg/robot/manager.go +++ b/src/testing/pkg/robot/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package robot @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, m *model.Robot) (int64, error) { ret := _m.Called(ctx, m) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.Robot) (int64, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, m *model.Robot) (int64, error) { func (_m *Manager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int64) error { func (_m *Manager) DeleteByProjectID(ctx context.Context, projectID int64) error { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for DeleteByProjectID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, projectID) @@ -96,6 +112,10 @@ func (_m *Manager) DeleteByProjectID(ctx context.Context, projectID int64) error func (_m *Manager) Get(ctx context.Context, id int64) (*model.Robot, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*model.Robot, error)); ok { @@ -122,6 +142,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*model.Robot, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.Robot, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.Robot var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.Robot, error)); ok { @@ -155,6 +179,10 @@ func (_m *Manager) Update(ctx context.Context, m *model.Robot, props ...string) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.Robot, ...string) error); ok { r0 = rf(ctx, m, props...) diff --git a/src/testing/pkg/scan/export/artifact_digest_calculator.go b/src/testing/pkg/scan/export/artifact_digest_calculator.go index 0a3436672..150c11cfd 100644 --- a/src/testing/pkg/scan/export/artifact_digest_calculator.go +++ b/src/testing/pkg/scan/export/artifact_digest_calculator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package export @@ -17,6 +17,10 @@ type ArtifactDigestCalculator struct { func (_m *ArtifactDigestCalculator) Calculate(fileName string) (digest.Digest, error) { ret := _m.Called(fileName) + if len(ret) == 0 { + panic("no return value specified for Calculate") + } + var r0 digest.Digest var r1 error if rf, ok := ret.Get(0).(func(string) (digest.Digest, error)); ok { diff --git a/src/testing/pkg/scan/export/filter_processor.go b/src/testing/pkg/scan/export/filter_processor.go index fac0dbfff..4087eba62 100644 --- a/src/testing/pkg/scan/export/filter_processor.go +++ b/src/testing/pkg/scan/export/filter_processor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package export @@ -19,6 +19,10 @@ type FilterProcessor struct { func (_m *FilterProcessor) ProcessLabelFilter(ctx context.Context, labelIDs []int64, arts []*artifact.Artifact) ([]*artifact.Artifact, error) { ret := _m.Called(ctx, labelIDs, arts) + if len(ret) == 0 { + panic("no return value specified for ProcessLabelFilter") + } + var r0 []*artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, []int64, []*artifact.Artifact) ([]*artifact.Artifact, error)); ok { @@ -45,6 +49,10 @@ func (_m *FilterProcessor) ProcessLabelFilter(ctx context.Context, labelIDs []in func (_m *FilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter string, projectIds []int64) ([]int64, error) { ret := _m.Called(ctx, filter, projectIds) + if len(ret) == 0 { + panic("no return value specified for ProcessRepositoryFilter") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []int64) ([]int64, error)); ok { @@ -71,6 +79,10 @@ func (_m *FilterProcessor) ProcessRepositoryFilter(ctx context.Context, filter s func (_m *FilterProcessor) ProcessTagFilter(ctx context.Context, filter string, repositoryIds []int64) ([]*artifact.Artifact, error) { ret := _m.Called(ctx, filter, repositoryIds) + if len(ret) == 0 { + panic("no return value specified for ProcessTagFilter") + } + var r0 []*artifact.Artifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, []int64) ([]*artifact.Artifact, error)); ok { diff --git a/src/testing/pkg/scan/export/manager.go b/src/testing/pkg/scan/export/manager.go index 3261f9b50..158ed47ad 100644 --- a/src/testing/pkg/scan/export/manager.go +++ b/src/testing/pkg/scan/export/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package export @@ -18,6 +18,10 @@ type Manager struct { func (_m *Manager) Fetch(ctx context.Context, params export.Params) ([]export.Data, error) { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for Fetch") + } + var r0 []export.Data var r1 error if rf, ok := ret.Get(0).(func(context.Context, export.Params) ([]export.Data, error)); ok { diff --git a/src/testing/pkg/scan/report/manager.go b/src/testing/pkg/scan/report/manager.go index 768933506..4a50114f4 100644 --- a/src/testing/pkg/scan/report/manager.go +++ b/src/testing/pkg/scan/report/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package report @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Create(ctx context.Context, r *scan.Report) (string, error) { ret := _m.Called(ctx, r) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context, *scan.Report) (string, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Create(ctx context.Context, r *scan.Report) (string, error) { func (_m *Manager) Delete(ctx context.Context, uuid string) error { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, uuid) @@ -65,6 +73,10 @@ func (_m *Manager) DeleteByDigests(ctx context.Context, digests ...string) error _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DeleteByDigests") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, ...string) error); ok { r0 = rf(ctx, digests...) @@ -79,6 +91,10 @@ func (_m *Manager) DeleteByDigests(ctx context.Context, digests ...string) error func (_m *Manager) GetBy(ctx context.Context, digest string, registrationUUID string, mimeTypes []string) ([]*scan.Report, error) { ret := _m.Called(ctx, digest, registrationUUID, mimeTypes) + if len(ret) == 0 { + panic("no return value specified for GetBy") + } + var r0 []*scan.Report var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []string) ([]*scan.Report, error)); ok { @@ -105,6 +121,10 @@ func (_m *Manager) GetBy(ctx context.Context, digest string, registrationUUID st func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*scan.Report, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*scan.Report var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*scan.Report, error)); ok { @@ -138,6 +158,10 @@ func (_m *Manager) Update(ctx context.Context, r *scan.Report, cols ...string) e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *scan.Report, ...string) error); ok { r0 = rf(ctx, r, cols...) @@ -152,6 +176,10 @@ func (_m *Manager) Update(ctx context.Context, r *scan.Report, cols ...string) e func (_m *Manager) UpdateReportData(ctx context.Context, uuid string, _a2 string) error { ret := _m.Called(ctx, uuid, _a2) + if len(ret) == 0 { + panic("no return value specified for UpdateReportData") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { r0 = rf(ctx, uuid, _a2) diff --git a/src/testing/pkg/scan/rest/v1/client.go b/src/testing/pkg/scan/rest/v1/client.go index 1f17ff8e6..b17c21e71 100644 --- a/src/testing/pkg/scan/rest/v1/client.go +++ b/src/testing/pkg/scan/rest/v1/client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package v1 @@ -16,6 +16,10 @@ type Client struct { func (_m *Client) GetMetadata() (*v1.ScannerAdapterMetadata, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetMetadata") + } + var r0 *v1.ScannerAdapterMetadata var r1 error if rf, ok := ret.Get(0).(func() (*v1.ScannerAdapterMetadata, error)); ok { @@ -42,6 +46,10 @@ func (_m *Client) GetMetadata() (*v1.ScannerAdapterMetadata, error) { func (_m *Client) GetScanReport(scanRequestID string, reportMIMEType string) (string, error) { ret := _m.Called(scanRequestID, reportMIMEType) + if len(ret) == 0 { + panic("no return value specified for GetScanReport") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { @@ -66,6 +74,10 @@ func (_m *Client) GetScanReport(scanRequestID string, reportMIMEType string) (st func (_m *Client) SubmitScan(req *v1.ScanRequest) (*v1.ScanResponse, error) { ret := _m.Called(req) + if len(ret) == 0 { + panic("no return value specified for SubmitScan") + } + var r0 *v1.ScanResponse var r1 error if rf, ok := ret.Get(0).(func(*v1.ScanRequest) (*v1.ScanResponse, error)); ok { diff --git a/src/testing/pkg/scan/rest/v1/client_pool.go b/src/testing/pkg/scan/rest/v1/client_pool.go index f662ebd5e..6533473a8 100644 --- a/src/testing/pkg/scan/rest/v1/client_pool.go +++ b/src/testing/pkg/scan/rest/v1/client_pool.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package v1 @@ -16,6 +16,10 @@ type ClientPool struct { func (_m *ClientPool) Get(url string, authType string, accessCredential string, skipCertVerify bool) (v1.Client, error) { ret := _m.Called(url, authType, accessCredential, skipCertVerify) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 v1.Client var r1 error if rf, ok := ret.Get(0).(func(string, string, string, bool) (v1.Client, error)); ok { diff --git a/src/testing/pkg/scan/rest/v1/request_resolver.go b/src/testing/pkg/scan/rest/v1/request_resolver.go index 0f8ad927e..f5e67b78e 100644 --- a/src/testing/pkg/scan/rest/v1/request_resolver.go +++ b/src/testing/pkg/scan/rest/v1/request_resolver.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package v1 diff --git a/src/testing/pkg/scan/rest/v1/response_handler.go b/src/testing/pkg/scan/rest/v1/response_handler.go index 632f6c336..690934146 100644 --- a/src/testing/pkg/scan/rest/v1/response_handler.go +++ b/src/testing/pkg/scan/rest/v1/response_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package v1 @@ -17,6 +17,10 @@ type responseHandler struct { func (_m *responseHandler) Execute(code int, resp *http.Response) ([]byte, error) { ret := _m.Called(code, resp) + if len(ret) == 0 { + panic("no return value specified for Execute") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(int, *http.Response) ([]byte, error)); ok { diff --git a/src/testing/pkg/scan/scanner/manager.go b/src/testing/pkg/scan/scanner/manager.go index aaadd9f7d..08890ace3 100644 --- a/src/testing/pkg/scan/scanner/manager.go +++ b/src/testing/pkg/scan/scanner/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scanner @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, registration *daoscanner.Registration) (string, error) { ret := _m.Called(ctx, registration) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context, *daoscanner.Registration) (string, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, registration *daoscanner.Registra func (_m *Manager) DefaultScannerUUID(ctx context.Context) (string, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for DefaultScannerUUID") + } + var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context) (string, error)); ok { @@ -92,6 +104,10 @@ func (_m *Manager) DefaultScannerUUID(ctx context.Context) (string, error) { func (_m *Manager) Delete(ctx context.Context, registrationUUID string) error { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, registrationUUID) @@ -106,6 +122,10 @@ func (_m *Manager) Delete(ctx context.Context, registrationUUID string) error { func (_m *Manager) Get(ctx context.Context, registrationUUID string) (*daoscanner.Registration, error) { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *daoscanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*daoscanner.Registration, error)); ok { @@ -132,6 +152,10 @@ func (_m *Manager) Get(ctx context.Context, registrationUUID string) (*daoscanne func (_m *Manager) GetDefault(ctx context.Context) (*daoscanner.Registration, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetDefault") + } + var r0 *daoscanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*daoscanner.Registration, error)); ok { @@ -158,6 +182,10 @@ func (_m *Manager) GetDefault(ctx context.Context) (*daoscanner.Registration, er func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*daoscanner.Registration, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*daoscanner.Registration var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*daoscanner.Registration, error)); ok { @@ -184,6 +212,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*daoscanner.Regi func (_m *Manager) SetAsDefault(ctx context.Context, registrationUUID string) error { ret := _m.Called(ctx, registrationUUID) + if len(ret) == 0 { + panic("no return value specified for SetAsDefault") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, registrationUUID) @@ -198,6 +230,10 @@ func (_m *Manager) SetAsDefault(ctx context.Context, registrationUUID string) er func (_m *Manager) Update(ctx context.Context, registration *daoscanner.Registration) error { ret := _m.Called(ctx, registration) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *daoscanner.Registration) error); ok { r0 = rf(ctx, registration) diff --git a/src/testing/pkg/scheduler/scheduler.go b/src/testing/pkg/scheduler/scheduler.go index 437c7ca19..0550dcdf4 100644 --- a/src/testing/pkg/scheduler/scheduler.go +++ b/src/testing/pkg/scheduler/scheduler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package scheduler @@ -20,6 +20,10 @@ type Scheduler struct { func (_m *Scheduler) CountSchedules(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for CountSchedules") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Scheduler) CountSchedules(ctx context.Context, query *q.Query) (int64, func (_m *Scheduler) GetSchedule(ctx context.Context, id int64) (*scheduler.Schedule, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetSchedule") + } + var r0 *scheduler.Schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*scheduler.Schedule, error)); ok { @@ -70,6 +78,10 @@ func (_m *Scheduler) GetSchedule(ctx context.Context, id int64) (*scheduler.Sche func (_m *Scheduler) ListSchedules(ctx context.Context, query *q.Query) ([]*scheduler.Schedule, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListSchedules") + } + var r0 []*scheduler.Schedule var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*scheduler.Schedule, error)); ok { @@ -96,6 +108,10 @@ func (_m *Scheduler) ListSchedules(ctx context.Context, query *q.Query) ([]*sche func (_m *Scheduler) Schedule(ctx context.Context, vendorType string, vendorID int64, cronType string, cron string, callbackFuncName string, callbackFuncParams interface{}, extraAttrs map[string]interface{}) (int64, error) { ret := _m.Called(ctx, vendorType, vendorID, cronType, cron, callbackFuncName, callbackFuncParams, extraAttrs) + if len(ret) == 0 { + panic("no return value specified for Schedule") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, string, string, string, interface{}, map[string]interface{}) (int64, error)); ok { @@ -120,6 +136,10 @@ func (_m *Scheduler) Schedule(ctx context.Context, vendorType string, vendorID i func (_m *Scheduler) UnScheduleByID(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for UnScheduleByID") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -134,6 +154,10 @@ func (_m *Scheduler) UnScheduleByID(ctx context.Context, id int64) error { func (_m *Scheduler) UnScheduleByVendor(ctx context.Context, vendorType string, vendorID int64) error { ret := _m.Called(ctx, vendorType, vendorID) + if len(ret) == 0 { + panic("no return value specified for UnScheduleByVendor") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, vendorType, vendorID) diff --git a/src/testing/pkg/securityhub/manager.go b/src/testing/pkg/securityhub/manager.go index 9b47578c0..5050fb40d 100644 --- a/src/testing/pkg/securityhub/manager.go +++ b/src/testing/pkg/securityhub/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package securityhub @@ -22,6 +22,10 @@ type Manager struct { func (_m *Manager) DangerousArtifacts(ctx context.Context, scannerUUID string, projectID int64, query *q.Query) ([]*model.DangerousArtifact, error) { ret := _m.Called(ctx, scannerUUID, projectID, query) + if len(ret) == 0 { + panic("no return value specified for DangerousArtifacts") + } + var r0 []*model.DangerousArtifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *q.Query) ([]*model.DangerousArtifact, error)); ok { @@ -48,6 +52,10 @@ func (_m *Manager) DangerousArtifacts(ctx context.Context, scannerUUID string, p func (_m *Manager) DangerousCVEs(ctx context.Context, scannerUUID string, projectID int64, query *q.Query) ([]*scan.VulnerabilityRecord, error) { ret := _m.Called(ctx, scannerUUID, projectID, query) + if len(ret) == 0 { + panic("no return value specified for DangerousCVEs") + } + var r0 []*scan.VulnerabilityRecord var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *q.Query) ([]*scan.VulnerabilityRecord, error)); ok { @@ -74,6 +82,10 @@ func (_m *Manager) DangerousCVEs(ctx context.Context, scannerUUID string, projec func (_m *Manager) ListVuls(ctx context.Context, scannerUUID string, projectID int64, query *q.Query) ([]*model.VulnerabilityItem, error) { ret := _m.Called(ctx, scannerUUID, projectID, query) + if len(ret) == 0 { + panic("no return value specified for ListVuls") + } + var r0 []*model.VulnerabilityItem var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *q.Query) ([]*model.VulnerabilityItem, error)); ok { @@ -100,6 +112,10 @@ func (_m *Manager) ListVuls(ctx context.Context, scannerUUID string, projectID i func (_m *Manager) ScannedArtifactsCount(ctx context.Context, scannerUUID string, projectID int64, query *q.Query) (int64, error) { ret := _m.Called(ctx, scannerUUID, projectID, query) + if len(ret) == 0 { + panic("no return value specified for ScannedArtifactsCount") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *q.Query) (int64, error)); ok { @@ -124,6 +140,10 @@ func (_m *Manager) ScannedArtifactsCount(ctx context.Context, scannerUUID string func (_m *Manager) Summary(ctx context.Context, scannerUUID string, projectID int64, query *q.Query) (*model.Summary, error) { ret := _m.Called(ctx, scannerUUID, projectID, query) + if len(ret) == 0 { + panic("no return value specified for Summary") + } + var r0 *model.Summary var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, *q.Query) (*model.Summary, error)); ok { @@ -150,6 +170,10 @@ func (_m *Manager) Summary(ctx context.Context, scannerUUID string, projectID in func (_m *Manager) TotalArtifactsCount(ctx context.Context, projectID int64) (int64, error) { ret := _m.Called(ctx, projectID) + if len(ret) == 0 { + panic("no return value specified for TotalArtifactsCount") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (int64, error)); ok { @@ -174,6 +198,10 @@ func (_m *Manager) TotalArtifactsCount(ctx context.Context, projectID int64) (in func (_m *Manager) TotalVuls(ctx context.Context, scannerUUID string, projectID int64, tuneCount bool, query *q.Query) (int64, error) { ret := _m.Called(ctx, scannerUUID, projectID, tuneCount, query) + if len(ret) == 0 { + panic("no return value specified for TotalVuls") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, bool, *q.Query) (int64, error)); ok { diff --git a/src/testing/pkg/systemartifact/cleanup/selector.go b/src/testing/pkg/systemartifact/cleanup/selector.go index 5ca86ca76..68b3f8f20 100644 --- a/src/testing/pkg/systemartifact/cleanup/selector.go +++ b/src/testing/pkg/systemartifact/cleanup/selector.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package cleanup @@ -20,6 +20,10 @@ type Selector struct { func (_m *Selector) List(ctx context.Context) ([]*model.SystemArtifact, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.SystemArtifact var r1 error if rf, ok := ret.Get(0).(func(context.Context) ([]*model.SystemArtifact, error)); ok { @@ -46,6 +50,10 @@ func (_m *Selector) List(ctx context.Context) ([]*model.SystemArtifact, error) { func (_m *Selector) ListWithFilters(ctx context.Context, query *q.Query) ([]*model.SystemArtifact, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for ListWithFilters") + } + var r0 []*model.SystemArtifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.SystemArtifact, error)); ok { diff --git a/src/testing/pkg/systemartifact/dao/dao.go b/src/testing/pkg/systemartifact/dao/dao.go index 3222028dd..1d49abae3 100644 --- a/src/testing/pkg/systemartifact/dao/dao.go +++ b/src/testing/pkg/systemartifact/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Create(ctx context.Context, systemArtifact *model.SystemArtifact) (int64, error) { ret := _m.Called(ctx, systemArtifact) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.SystemArtifact) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Create(ctx context.Context, systemArtifact *model.SystemArtifact) func (_m *DAO) Delete(ctx context.Context, vendor string, repository string, digest string) error { ret := _m.Called(ctx, vendor, repository, digest) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok { r0 = rf(ctx, vendor, repository, digest) @@ -59,6 +67,10 @@ func (_m *DAO) Delete(ctx context.Context, vendor string, repository string, dig func (_m *DAO) Get(ctx context.Context, vendor string, repository string, digest string) (*model.SystemArtifact, error) { ret := _m.Called(ctx, vendor, repository, digest) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.SystemArtifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*model.SystemArtifact, error)); ok { @@ -85,6 +97,10 @@ func (_m *DAO) Get(ctx context.Context, vendor string, repository string, digest func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.SystemArtifact, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.SystemArtifact var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.SystemArtifact, error)); ok { @@ -111,6 +127,10 @@ func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*model.SystemArtifac func (_m *DAO) Size(ctx context.Context) (int64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Size") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok { diff --git a/src/testing/pkg/systemartifact/manager.go b/src/testing/pkg/systemartifact/manager.go index 392d2803b..1af03bee8 100644 --- a/src/testing/pkg/systemartifact/manager.go +++ b/src/testing/pkg/systemartifact/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package systemartifact @@ -22,6 +22,10 @@ type Manager struct { func (_m *Manager) Cleanup(ctx context.Context) (int64, int64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Cleanup") + } + var r0 int64 var r1 int64 var r2 error @@ -53,6 +57,10 @@ func (_m *Manager) Cleanup(ctx context.Context) (int64, int64, error) { func (_m *Manager) Create(ctx context.Context, artifactRecord *model.SystemArtifact, reader io.Reader) (int64, error) { ret := _m.Called(ctx, artifactRecord, reader) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *model.SystemArtifact, io.Reader) (int64, error)); ok { @@ -77,6 +85,10 @@ func (_m *Manager) Create(ctx context.Context, artifactRecord *model.SystemArtif func (_m *Manager) Delete(ctx context.Context, vendor string, repository string, digest string) error { ret := _m.Called(ctx, vendor, repository, digest) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok { r0 = rf(ctx, vendor, repository, digest) @@ -91,6 +103,10 @@ func (_m *Manager) Delete(ctx context.Context, vendor string, repository string, func (_m *Manager) Exists(ctx context.Context, vendor string, repository string, digest string) (bool, error) { ret := _m.Called(ctx, vendor, repository, digest) + if len(ret) == 0 { + panic("no return value specified for Exists") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (bool, error)); ok { @@ -115,6 +131,10 @@ func (_m *Manager) Exists(ctx context.Context, vendor string, repository string, func (_m *Manager) GetCleanupCriteria(vendor string, artifactType string) systemartifact.Selector { ret := _m.Called(vendor, artifactType) + if len(ret) == 0 { + panic("no return value specified for GetCleanupCriteria") + } + var r0 systemartifact.Selector if rf, ok := ret.Get(0).(func(string, string) systemartifact.Selector); ok { r0 = rf(vendor, artifactType) @@ -131,6 +151,10 @@ func (_m *Manager) GetCleanupCriteria(vendor string, artifactType string) system func (_m *Manager) GetStorageSize(ctx context.Context) (int64, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetStorageSize") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context) (int64, error)); ok { @@ -155,6 +179,10 @@ func (_m *Manager) GetStorageSize(ctx context.Context) (int64, error) { func (_m *Manager) GetSystemArtifactProjectNames() []string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetSystemArtifactProjectNames") + } + var r0 []string if rf, ok := ret.Get(0).(func() []string); ok { r0 = rf() @@ -171,6 +199,10 @@ func (_m *Manager) GetSystemArtifactProjectNames() []string { func (_m *Manager) Read(ctx context.Context, vendor string, repository string, digest string) (io.ReadCloser, error) { ret := _m.Called(ctx, vendor, repository, digest) + if len(ret) == 0 { + panic("no return value specified for Read") + } + var r0 io.ReadCloser var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (io.ReadCloser, error)); ok { diff --git a/src/testing/pkg/task/execution_manager.go b/src/testing/pkg/task/execution_manager.go index 890aa6d4b..7960eea18 100644 --- a/src/testing/pkg/task/execution_manager.go +++ b/src/testing/pkg/task/execution_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -22,6 +22,10 @@ type ExecutionManager struct { func (_m *ExecutionManager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -53,6 +57,10 @@ func (_m *ExecutionManager) Create(ctx context.Context, vendorType string, vendo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, int64, string, ...map[string]interface{}) (int64, error)); ok { @@ -77,6 +85,10 @@ func (_m *ExecutionManager) Create(ctx context.Context, vendorType string, vendo func (_m *ExecutionManager) Delete(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -91,6 +103,10 @@ func (_m *ExecutionManager) Delete(ctx context.Context, id int64) error { func (_m *ExecutionManager) DeleteByVendor(ctx context.Context, vendorType string, vendorID int64) error { ret := _m.Called(ctx, vendorType, vendorID) + if len(ret) == 0 { + panic("no return value specified for DeleteByVendor") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, int64) error); ok { r0 = rf(ctx, vendorType, vendorID) @@ -105,6 +121,10 @@ func (_m *ExecutionManager) DeleteByVendor(ctx context.Context, vendorType strin func (_m *ExecutionManager) Get(ctx context.Context, id int64) (*task.Execution, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *task.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*task.Execution, error)); ok { @@ -131,6 +151,10 @@ func (_m *ExecutionManager) Get(ctx context.Context, id int64) (*task.Execution, func (_m *ExecutionManager) List(ctx context.Context, query *q.Query) ([]*task.Execution, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*task.Execution var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*task.Execution, error)); ok { @@ -157,6 +181,10 @@ func (_m *ExecutionManager) List(ctx context.Context, query *q.Query) ([]*task.E func (_m *ExecutionManager) MarkDone(ctx context.Context, id int64, message string) error { ret := _m.Called(ctx, id, message) + if len(ret) == 0 { + panic("no return value specified for MarkDone") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, id, message) @@ -171,6 +199,10 @@ func (_m *ExecutionManager) MarkDone(ctx context.Context, id int64, message stri func (_m *ExecutionManager) MarkError(ctx context.Context, id int64, message string) error { ret := _m.Called(ctx, id, message) + if len(ret) == 0 { + panic("no return value specified for MarkError") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, string) error); ok { r0 = rf(ctx, id, message) @@ -185,6 +217,10 @@ func (_m *ExecutionManager) MarkError(ctx context.Context, id int64, message str func (_m *ExecutionManager) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -199,6 +235,10 @@ func (_m *ExecutionManager) Stop(ctx context.Context, id int64) error { func (_m *ExecutionManager) StopAndWait(ctx context.Context, id int64, timeout time.Duration) error { ret := _m.Called(ctx, id, timeout) + if len(ret) == 0 { + panic("no return value specified for StopAndWait") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, time.Duration) error); ok { r0 = rf(ctx, id, timeout) @@ -213,6 +253,10 @@ func (_m *ExecutionManager) StopAndWait(ctx context.Context, id int64, timeout t func (_m *ExecutionManager) StopAndWaitWithError(ctx context.Context, id int64, timeout time.Duration, origError error) error { ret := _m.Called(ctx, id, timeout, origError) + if len(ret) == 0 { + panic("no return value specified for StopAndWaitWithError") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, time.Duration, error) error); ok { r0 = rf(ctx, id, timeout, origError) @@ -227,6 +271,10 @@ func (_m *ExecutionManager) StopAndWaitWithError(ctx context.Context, id int64, func (_m *ExecutionManager) UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs map[string]interface{}) error { ret := _m.Called(ctx, id, extraAttrs) + if len(ret) == 0 { + panic("no return value specified for UpdateExtraAttrs") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, map[string]interface{}) error); ok { r0 = rf(ctx, id, extraAttrs) diff --git a/src/testing/pkg/task/manager.go b/src/testing/pkg/task/manager.go index 9e8dbbd16..d1172aa69 100644 --- a/src/testing/pkg/task/manager.go +++ b/src/testing/pkg/task/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package task @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -51,6 +55,10 @@ func (_m *Manager) Create(ctx context.Context, executionID int64, job *task.Job, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64, *task.Job, ...map[string]interface{}) (int64, error)); ok { @@ -75,6 +83,10 @@ func (_m *Manager) Create(ctx context.Context, executionID int64, job *task.Job, func (_m *Manager) ExecutionIDsByVendorAndStatus(ctx context.Context, vendorType string, status string) ([]int64, error) { ret := _m.Called(ctx, vendorType, status) + if len(ret) == 0 { + panic("no return value specified for ExecutionIDsByVendorAndStatus") + } + var r0 []int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]int64, error)); ok { @@ -101,6 +113,10 @@ func (_m *Manager) ExecutionIDsByVendorAndStatus(ctx context.Context, vendorType func (_m *Manager) Get(ctx context.Context, id int64) (*task.Task, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) (*task.Task, error)); ok { @@ -127,6 +143,10 @@ func (_m *Manager) Get(ctx context.Context, id int64) (*task.Task, error) { func (_m *Manager) GetLog(ctx context.Context, id int64) ([]byte, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetLog") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, int64) ([]byte, error)); ok { @@ -153,6 +173,10 @@ func (_m *Manager) GetLog(ctx context.Context, id int64) ([]byte, error) { func (_m *Manager) GetLogByJobID(ctx context.Context, jobID string) ([]byte, error) { ret := _m.Called(ctx, jobID) + if len(ret) == 0 { + panic("no return value specified for GetLogByJobID") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]byte, error)); ok { @@ -179,6 +203,10 @@ func (_m *Manager) GetLogByJobID(ctx context.Context, jobID string) ([]byte, err func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*task.Task, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*task.Task, error)); ok { @@ -205,6 +233,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*task.Task, erro func (_m *Manager) ListScanTasksByReportUUID(ctx context.Context, uuid string) ([]*task.Task, error) { ret := _m.Called(ctx, uuid) + if len(ret) == 0 { + panic("no return value specified for ListScanTasksByReportUUID") + } + var r0 []*task.Task var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) ([]*task.Task, error)); ok { @@ -231,6 +263,10 @@ func (_m *Manager) ListScanTasksByReportUUID(ctx context.Context, uuid string) ( func (_m *Manager) Stop(ctx context.Context, id int64) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Stop") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok { r0 = rf(ctx, id) @@ -252,6 +288,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *task.Task, props ...string) _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *task.Task, ...string) error); ok { r0 = rf(ctx, _a1, props...) @@ -266,6 +306,10 @@ func (_m *Manager) Update(ctx context.Context, _a1 *task.Task, props ...string) func (_m *Manager) UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs map[string]interface{}) error { ret := _m.Called(ctx, id, extraAttrs) + if len(ret) == 0 { + panic("no return value specified for UpdateExtraAttrs") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int64, map[string]interface{}) error); ok { r0 = rf(ctx, id, extraAttrs) @@ -280,6 +324,10 @@ func (_m *Manager) UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs ma func (_m *Manager) UpdateStatusInBatch(ctx context.Context, jobIDs []string, status string, batchSize int) error { ret := _m.Called(ctx, jobIDs, status, batchSize) + if len(ret) == 0 { + panic("no return value specified for UpdateStatusInBatch") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, []string, string, int) error); ok { r0 = rf(ctx, jobIDs, status, batchSize) diff --git a/src/testing/pkg/user/dao/dao.go b/src/testing/pkg/user/dao/dao.go index 89bcb213a..f46ec54d5 100644 --- a/src/testing/pkg/user/dao/dao.go +++ b/src/testing/pkg/user/dao/dao.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package dao @@ -21,6 +21,10 @@ type DAO struct { func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -45,6 +49,10 @@ func (_m *DAO) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *DAO) Create(ctx context.Context, user *models.User) (int, error) { ret := _m.Called(ctx, user) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *models.User) (int, error)); ok { @@ -69,6 +77,10 @@ func (_m *DAO) Create(ctx context.Context, user *models.User) (int, error) { func (_m *DAO) Delete(ctx context.Context, userID int) error { ret := _m.Called(ctx, userID) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, userID) @@ -83,6 +95,10 @@ func (_m *DAO) Delete(ctx context.Context, userID int) error { func (_m *DAO) List(ctx context.Context, query *q.Query) ([]*models.User, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*models.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*models.User, error)); ok { @@ -116,6 +132,10 @@ func (_m *DAO) Update(ctx context.Context, user *models.User, props ...string) e _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *models.User, ...string) error); ok { r0 = rf(ctx, user, props...) diff --git a/src/testing/pkg/user/manager.go b/src/testing/pkg/user/manager.go index 83b4f9d77..cee323805 100644 --- a/src/testing/pkg/user/manager.go +++ b/src/testing/pkg/user/manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package user @@ -30,6 +30,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query, options ...models. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, ...models.Option) (int64, error)); ok { @@ -54,6 +58,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query, options ...models. func (_m *Manager) Create(ctx context.Context, _a1 *commonmodels.User) (int, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, *commonmodels.User) (int, error)); ok { @@ -78,6 +86,10 @@ func (_m *Manager) Create(ctx context.Context, _a1 *commonmodels.User) (int, err func (_m *Manager) Delete(ctx context.Context, id int) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, id) @@ -92,6 +104,10 @@ func (_m *Manager) Delete(ctx context.Context, id int) error { func (_m *Manager) DeleteGDPR(ctx context.Context, id int) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteGDPR") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, id) @@ -106,6 +122,10 @@ func (_m *Manager) DeleteGDPR(ctx context.Context, id int) error { func (_m *Manager) GenerateCheckSum(in string) string { ret := _m.Called(in) + if len(ret) == 0 { + panic("no return value specified for GenerateCheckSum") + } + var r0 string if rf, ok := ret.Get(0).(func(string) string); ok { r0 = rf(in) @@ -120,6 +140,10 @@ func (_m *Manager) GenerateCheckSum(in string) string { func (_m *Manager) Get(ctx context.Context, id int) (*commonmodels.User, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *commonmodels.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, int) (*commonmodels.User, error)); ok { @@ -146,6 +170,10 @@ func (_m *Manager) Get(ctx context.Context, id int) (*commonmodels.User, error) func (_m *Manager) GetByName(ctx context.Context, username string) (*commonmodels.User, error) { ret := _m.Called(ctx, username) + if len(ret) == 0 { + panic("no return value specified for GetByName") + } + var r0 *commonmodels.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*commonmodels.User, error)); ok { @@ -179,6 +207,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query, options ...models.O _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 commonmodels.Users var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query, ...models.Option) (commonmodels.Users, error)); ok { @@ -205,6 +237,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query, options ...models.O func (_m *Manager) MatchLocalPassword(ctx context.Context, username string, password string) (*commonmodels.User, error) { ret := _m.Called(ctx, username, password) + if len(ret) == 0 { + panic("no return value specified for MatchLocalPassword") + } + var r0 *commonmodels.User var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (*commonmodels.User, error)); ok { @@ -231,6 +267,10 @@ func (_m *Manager) MatchLocalPassword(ctx context.Context, username string, pass func (_m *Manager) Onboard(ctx context.Context, _a1 *commonmodels.User) error { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for Onboard") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *commonmodels.User) error); ok { r0 = rf(ctx, _a1) @@ -245,6 +285,10 @@ func (_m *Manager) Onboard(ctx context.Context, _a1 *commonmodels.User) error { func (_m *Manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) error { ret := _m.Called(ctx, id, admin) + if len(ret) == 0 { + panic("no return value specified for SetSysAdminFlag") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, bool) error); ok { r0 = rf(ctx, id, admin) @@ -259,6 +303,10 @@ func (_m *Manager) SetSysAdminFlag(ctx context.Context, id int, admin bool) erro func (_m *Manager) UpdatePassword(ctx context.Context, id int, newPassword string) error { ret := _m.Called(ctx, id, newPassword) + if len(ret) == 0 { + panic("no return value specified for UpdatePassword") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok { r0 = rf(ctx, id, newPassword) @@ -280,6 +328,10 @@ func (_m *Manager) UpdateProfile(ctx context.Context, _a1 *commonmodels.User, co _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateProfile") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *commonmodels.User, ...string) error); ok { r0 = rf(ctx, _a1, col...) diff --git a/src/testing/pkg/usergroup/fake_usergroup_manager.go b/src/testing/pkg/usergroup/fake_usergroup_manager.go index 8d2b83a63..8f1bf2d8f 100644 --- a/src/testing/pkg/usergroup/fake_usergroup_manager.go +++ b/src/testing/pkg/usergroup/fake_usergroup_manager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.35.4. DO NOT EDIT. +// Code generated by mockery v2.42.2. DO NOT EDIT. package usergroup @@ -20,6 +20,10 @@ type Manager struct { func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for Count") + } + var r0 int64 var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok { @@ -44,6 +48,10 @@ func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) { func (_m *Manager) Create(ctx context.Context, userGroup model.UserGroup) (int, error) { ret := _m.Called(ctx, userGroup) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 int var r1 error if rf, ok := ret.Get(0).(func(context.Context, model.UserGroup) (int, error)); ok { @@ -68,6 +76,10 @@ func (_m *Manager) Create(ctx context.Context, userGroup model.UserGroup) (int, func (_m *Manager) Delete(ctx context.Context, id int) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int) error); ok { r0 = rf(ctx, id) @@ -82,6 +94,10 @@ func (_m *Manager) Delete(ctx context.Context, id int) error { func (_m *Manager) Get(ctx context.Context, id int) (*model.UserGroup, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *model.UserGroup var r1 error if rf, ok := ret.Get(0).(func(context.Context, int) (*model.UserGroup, error)); ok { @@ -108,6 +124,10 @@ func (_m *Manager) Get(ctx context.Context, id int) (*model.UserGroup, error) { func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.UserGroup, error) { ret := _m.Called(ctx, query) + if len(ret) == 0 { + panic("no return value specified for List") + } + var r0 []*model.UserGroup var r1 error if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*model.UserGroup, error)); ok { @@ -134,6 +154,10 @@ func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*model.UserGroup func (_m *Manager) Onboard(ctx context.Context, g *model.UserGroup) error { ret := _m.Called(ctx, g) + if len(ret) == 0 { + panic("no return value specified for Onboard") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, *model.UserGroup) error); ok { r0 = rf(ctx, g) @@ -148,6 +172,10 @@ func (_m *Manager) Onboard(ctx context.Context, g *model.UserGroup) error { func (_m *Manager) Populate(ctx context.Context, userGroups []model.UserGroup) ([]int, error) { ret := _m.Called(ctx, userGroups) + if len(ret) == 0 { + panic("no return value specified for Populate") + } + var r0 []int var r1 error if rf, ok := ret.Get(0).(func(context.Context, []model.UserGroup) ([]int, error)); ok { @@ -174,6 +202,10 @@ func (_m *Manager) Populate(ctx context.Context, userGroups []model.UserGroup) ( func (_m *Manager) UpdateName(ctx context.Context, id int, groupName string) error { ret := _m.Called(ctx, id, groupName) + if len(ret) == 0 { + panic("no return value specified for UpdateName") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, int, string) error); ok { r0 = rf(ctx, id, groupName) From 7e8032b14441f0dbd6e8b601620c3d7f3379f144 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Fri, 12 Apr 2024 13:46:29 +0800 Subject: [PATCH 014/100] bump golang to 1.22.2 (#20256) Signed-off-by: yminer replace go get to go install update go.mod --- .github/workflows/CI.yml | 20 +++++++++---------- .github/workflows/build-package.yml | 4 ++-- .github/workflows/codeql-analysis.yml | 3 +++ .github/workflows/conformance_test.yml | 2 +- CONTRIBUTING.md | 3 ++- Makefile | 2 +- make/photon/registry/Dockerfile.binary | 2 +- make/photon/trivy-adapter/Dockerfile.binary | 2 +- make/photon/trivy-adapter/builder.sh | 2 +- src/go.mod | 2 +- tests/ci/distro_installer.sh | 4 ++-- tests/ci/ut_install.sh | 22 ++++++++++----------- 12 files changed, 36 insertions(+), 32 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ea867a8ce..94ec2377b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -41,10 +41,10 @@ jobs: - ubuntu-latest timeout-minutes: 100 steps: - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: @@ -102,10 +102,10 @@ jobs: - ubuntu-latest timeout-minutes: 100 steps: - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: @@ -157,10 +157,10 @@ jobs: - ubuntu-latest timeout-minutes: 100 steps: - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: @@ -212,10 +212,10 @@ jobs: - ubuntu-latest timeout-minutes: 100 steps: - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: @@ -265,10 +265,10 @@ jobs: - ubuntu-latest timeout-minutes: 100 steps: - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: diff --git a/.github/workflows/build-package.yml b/.github/workflows/build-package.yml index 74f0b99ab..6bd029b70 100644 --- a/.github/workflows/build-package.yml +++ b/.github/workflows/build-package.yml @@ -23,10 +23,10 @@ jobs: with: version: '430.0.0' - run: gcloud info - - name: Set up Go 1.21 + - name: Set up Go 1.22 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - name: Setup Docker uses: docker-practice/actions-setup-docker@master diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9dfb26473..f97fcebcd 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,5 +47,8 @@ jobs: # make bootstrap # make release + # to make sure autobuild success, specifify golang version in go.mod + # https://github.com/github/codeql/issues/15647#issuecomment-2003768106 + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/conformance_test.yml b/.github/workflows/conformance_test.yml index 520f417f4..b1183ca80 100644 --- a/.github/workflows/conformance_test.yml +++ b/.github/workflows/conformance_test.yml @@ -28,7 +28,7 @@ jobs: - name: Set up Go 1.21 uses: actions/setup-go@v5 with: - go-version: 1.21.8 + go-version: 1.22.2 id: go - uses: actions/checkout@v3 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2af5023b8..a7cda9d19 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -164,7 +164,8 @@ Harbor backend is written in [Go](http://golang.org/). If you don't have a Harbo | 2.7 | 1.19.4 | | 2.8 | 1.20.6 | | 2.9 | 1.21.3 | -| 2.10 | 1.21.8 | +| 2.10 | 1.21.8 | +| 2.11 | 1.22.2 | Ensure your GOPATH and PATH have been configured in accordance with the Go environment instructions. diff --git a/Makefile b/Makefile index 16a532a52..e9dd6f7dd 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,7 @@ GOINSTALL=$(GOCMD) install GOTEST=$(GOCMD) test GODEP=$(GOTEST) -i GOFMT=gofmt -w -GOBUILDIMAGE=golang:1.21.8 +GOBUILDIMAGE=golang:1.22.2 GOBUILDPATHINCONTAINER=/harbor # go build diff --git a/make/photon/registry/Dockerfile.binary b/make/photon/registry/Dockerfile.binary index c18fb9e09..e5efd1013 100644 --- a/make/photon/registry/Dockerfile.binary +++ b/make/photon/registry/Dockerfile.binary @@ -1,4 +1,4 @@ -FROM golang:1.21.8 +FROM golang:1.22.2 ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution ENV BUILDTAGS include_oss include_gcs diff --git a/make/photon/trivy-adapter/Dockerfile.binary b/make/photon/trivy-adapter/Dockerfile.binary index 65bc9e9a5..fad616100 100644 --- a/make/photon/trivy-adapter/Dockerfile.binary +++ b/make/photon/trivy-adapter/Dockerfile.binary @@ -1,4 +1,4 @@ -FROM golang:1.21.8 +FROM golang:1.22.2 ADD . /go/src/github.com/aquasecurity/harbor-scanner-trivy/ WORKDIR /go/src/github.com/aquasecurity/harbor-scanner-trivy/ diff --git a/make/photon/trivy-adapter/builder.sh b/make/photon/trivy-adapter/builder.sh index debed09a5..e9eadf182 100755 --- a/make/photon/trivy-adapter/builder.sh +++ b/make/photon/trivy-adapter/builder.sh @@ -19,7 +19,7 @@ TEMP=$(mktemp -d ${TMPDIR-/tmp}/trivy-adapter.XXXXXX) git clone https://github.com/aquasecurity/harbor-scanner-trivy.git $TEMP cd $TEMP; git checkout $VERSION; cd - -echo "Building Trivy adapter binary based on golang:1.21.8..." +echo "Building Trivy adapter binary based on golang:1.22.2..." cp Dockerfile.binary $TEMP docker build -f $TEMP/Dockerfile.binary -t trivy-adapter-golang $TEMP diff --git a/src/go.mod b/src/go.mod index 7e439c773..00a328798 100644 --- a/src/go.mod +++ b/src/go.mod @@ -1,6 +1,6 @@ module github.com/goharbor/harbor/src -go 1.21 +go 1.22.2 require ( github.com/FZambia/sentinel v1.1.0 diff --git a/tests/ci/distro_installer.sh b/tests/ci/distro_installer.sh index ae10a5f85..af89f4471 100755 --- a/tests/ci/distro_installer.sh +++ b/tests/ci/distro_installer.sh @@ -3,5 +3,5 @@ set -x set -e -sudo make package_online GOBUILDTAGS="include_oss include_gcs" VERSIONTAG=dev-gitaction PKGVERSIONTAG=dev-gitaction UIVERSIONTAG=dev-gitaction GOBUILDIMAGE=golang:1.21.8 COMPILETAG=compile_golangimage TRIVYFLAG=true HTTPPROXY= PULL_BASE_FROM_DOCKERHUB=false -sudo make package_offline GOBUILDTAGS="include_oss include_gcs" VERSIONTAG=dev-gitaction PKGVERSIONTAG=dev-gitaction UIVERSIONTAG=dev-gitaction GOBUILDIMAGE=golang:1.21.8 COMPILETAG=compile_golangimage TRIVYFLAG=true HTTPPROXY= PULL_BASE_FROM_DOCKERHUB=false +sudo make package_online GOBUILDTAGS="include_oss include_gcs" VERSIONTAG=dev-gitaction PKGVERSIONTAG=dev-gitaction UIVERSIONTAG=dev-gitaction GOBUILDIMAGE=golang:1.22.2 COMPILETAG=compile_golangimage TRIVYFLAG=true HTTPPROXY= PULL_BASE_FROM_DOCKERHUB=false +sudo make package_offline GOBUILDTAGS="include_oss include_gcs" VERSIONTAG=dev-gitaction PKGVERSIONTAG=dev-gitaction UIVERSIONTAG=dev-gitaction GOBUILDIMAGE=golang:1.22.2 COMPILETAG=compile_golangimage TRIVYFLAG=true HTTPPROXY= PULL_BASE_FROM_DOCKERHUB=false diff --git a/tests/ci/ut_install.sh b/tests/ci/ut_install.sh index 38cfa8cf2..6702bb3aa 100755 --- a/tests/ci/ut_install.sh +++ b/tests/ci/ut_install.sh @@ -5,19 +5,19 @@ set -e sudo apt-get update && sudo apt-get install -y libldap2-dev sudo go env -w GO111MODULE=auto -go get github.com/docker/distribution -go get github.com/docker/libtrust -go get golang.org/x/lint/golint -go get github.com/GeertJohan/fgt -go get github.com/dghubble/sling -set +e -go get github.com/stretchr/testify -go get golang.org/x/tools/cmd/cover -go get github.com/mattn/goveralls -go get -u github.com/client9/misspell/cmd/misspell +pwd +# cd ./src +# go get github.com/docker/distribution@latest +# go get github.com/docker/libtrust@latest +# set +e +# go get github.com/stretchr/testify@v1.8.4 +go install golang.org/x/tools/cmd/cover@latest +go install github.com/mattn/goveralls@latest +go install github.com/client9/misspell/cmd/misspell@latest set -e +# cd ../ # binary will be $(go env GOPATH)/bin/golangci-lint -# go install/go get installation aren't guaranteed to work. We recommend using binary installation. +# go get installation aren't guaranteed to work. We recommend using binary installation. curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2 sudo service postgresql stop || echo no postgresql need to be stopped sleep 2 From 7465a29919da8ed4fa132bdbc217806369e3bf38 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Fri, 12 Apr 2024 20:12:46 +0800 Subject: [PATCH 015/100] add SBOM icon (#20270) Signed-off-by: yminer --- icons/sbom.png | Bin 0 -> 120331 bytes src/controller/icon/controller.go | 4 ++++ src/lib/icon/const.go | 1 + src/pkg/accessory/manager.go | 1 + 4 files changed, 6 insertions(+) create mode 100644 icons/sbom.png diff --git a/icons/sbom.png b/icons/sbom.png new file mode 100644 index 0000000000000000000000000000000000000000..8903142fb226fa6db1fde21590e1d63d84761371 GIT binary patch literal 120331 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%0D^gbM@X^j5vA`EBmtv*XLH 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 Date: Mon, 15 Apr 2024 16:47:55 +0800 Subject: [PATCH 016/100] Bump k8s.io/api from 0.29.0 to 0.29.3 in /src (#20205) Bumps [k8s.io/api](https://github.com/kubernetes/api) from 0.29.0 to 0.29.3. - [Commits](https://github.com/kubernetes/api/compare/v0.29.0...v0.29.3) --- updated-dependencies: - dependency-name: k8s.io/api dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shengwen YU --- src/go.mod | 6 +++--- src/go.sum | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/go.mod b/src/go.mod index 00a328798..fe3c17e60 100644 --- a/src/go.mod +++ b/src/go.mod @@ -72,8 +72,8 @@ require ( gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v2 v2.4.0 helm.sh/helm/v3 v3.14.2 - k8s.io/api v0.29.0 - k8s.io/apimachinery v0.29.0 + k8s.io/api v0.29.3 + k8s.io/apimachinery v0.29.3 k8s.io/client-go v0.29.0 sigs.k8s.io/yaml v1.4.0 ) @@ -115,7 +115,7 @@ require ( github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gorilla/securecookie v1.1.1 // indirect diff --git a/src/go.sum b/src/go.sum index 8081c0f92..b9cb6294f 100644 --- a/src/go.sum +++ b/src/go.sum @@ -260,8 +260,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.8.8 h1:f6cXq6RRfiyrOJEV7p3JhLDlmawGBVBBP1MggY8Mo4E= github.com/gomodule/redigo v1.8.8/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= @@ -703,6 +703,7 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -817,6 +818,7 @@ golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -825,6 +827,7 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -872,8 +875,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -968,10 +971,10 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= -k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= -k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= -k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= +k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= +k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= +k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= From b8392968ac93a759b7ef7076421350fea15b0f59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 18:18:53 +0800 Subject: [PATCH 017/100] Bump github.com/coreos/go-oidc/v3 from 3.9.0 to 3.10.0 in /src (#20202) Bumps [github.com/coreos/go-oidc/v3](https://github.com/coreos/go-oidc) from 3.9.0 to 3.10.0. - [Release notes](https://github.com/coreos/go-oidc/releases) - [Commits](https://github.com/coreos/go-oidc/compare/v3.9.0...v3.10.0) --- updated-dependencies: - dependency-name: github.com/coreos/go-oidc/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shengwen YU --- src/go.mod | 4 ++-- src/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/go.mod b/src/go.mod index fe3c17e60..9d09b5d5e 100644 --- a/src/go.mod +++ b/src/go.mod @@ -14,7 +14,7 @@ require ( github.com/casbin/casbin v1.9.1 github.com/cenkalti/backoff/v4 v4.2.1 github.com/cloudevents/sdk-go/v2 v2.15.2 - github.com/coreos/go-oidc/v3 v3.9.0 + github.com/coreos/go-oidc/v3 v3.10.0 github.com/dghubble/sling v1.1.0 github.com/docker/distribution v2.8.2+incompatible github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 @@ -107,7 +107,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/go-jose/go-jose/v3 v3.0.3 // indirect + github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.4 // indirect diff --git a/src/go.sum b/src/go.sum index b9cb6294f..03e8e2d96 100644 --- a/src/go.sum +++ b/src/go.sum @@ -112,8 +112,8 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= -github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= +github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= +github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -172,8 +172,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= -github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= +github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= From 79dbebd48d8df70d9d55c432ea96005451a933ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Apr 2024 19:04:22 +0800 Subject: [PATCH 018/100] Bump golang.org/x/oauth2 from 0.15.0 to 0.19.0 in /src (#20247) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.15.0 to 0.19.0. - [Commits](https://github.com/golang/oauth2/compare/v0.15.0...v0.19.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shengwen YU --- src/go.mod | 3 +-- src/go.sum | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/go.mod b/src/go.mod index 9d09b5d5e..e25f95586 100644 --- a/src/go.mod +++ b/src/go.mod @@ -65,7 +65,7 @@ require ( go.uber.org/ratelimit v0.2.0 golang.org/x/crypto v0.21.0 golang.org/x/net v0.22.0 - golang.org/x/oauth2 v0.15.0 + golang.org/x/oauth2 v0.19.0 golang.org/x/sync v0.6.0 golang.org/x/text v0.14.0 golang.org/x/time v0.5.0 @@ -174,7 +174,6 @@ require ( golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect google.golang.org/api v0.149.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect diff --git a/src/go.sum b/src/go.sum index 03e8e2d96..3d452134c 100644 --- a/src/go.sum +++ b/src/go.sum @@ -764,8 +764,8 @@ golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -888,8 +888,6 @@ google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= From a2507dc3fc7d0335123ff896bf0bd8b0b2274389 Mon Sep 17 00:00:00 2001 From: Iceber Gu Date: Mon, 15 Apr 2024 20:37:59 +0800 Subject: [PATCH 019/100] Sending signals by closing the channel (#17917) Signed-off-by: Iceber Gu --- src/cmd/migrate-patch/main.go | 7 +++++-- src/core/main.go | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cmd/migrate-patch/main.go b/src/cmd/migrate-patch/main.go index cb224ec5d..eb728b3a1 100644 --- a/src/cmd/migrate-patch/main.go +++ b/src/cmd/migrate-patch/main.go @@ -48,20 +48,23 @@ func main() { log.Fatalf("Failed to connect to Database, error: %v\n", err) } defer db.Close() - c := make(chan struct{}, 1) + + c := make(chan struct{}) go func() { + defer close(c) + err := db.Ping() for ; err != nil; err = db.Ping() { log.Println("Failed to Ping DB, sleep for 1 second.") time.Sleep(1 * time.Second) } - c <- struct{}{} }() select { case <-c: case <-time.After(30 * time.Second): log.Fatal("Failed to connect DB after 30 seconds, time out. \n") } + row := db.QueryRow(pgSQLCheckColStmt) var tblCount, colCount int if err := row.Scan(&tblCount, &colCount); err != nil { diff --git a/src/core/main.go b/src/core/main.go index cb2676135..ebc786d7e 100644 --- a/src/core/main.go +++ b/src/core/main.go @@ -104,14 +104,14 @@ func gracefulShutdown(closing, done chan struct{}, shutdowns ...func()) { signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) log.Infof("capture system signal %s, to close \"closing\" channel", <-signals) close(closing) - shutdownChan := make(chan struct{}, 1) + shutdownChan := make(chan struct{}) go func() { + defer close(shutdownChan) for _, s := range shutdowns { s() } <-done log.Infof("Goroutines exited normally") - shutdownChan <- struct{}{} }() select { case <-shutdownChan: From 938c80451359cce5058d5d096824eb0b125406e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:11:19 +0800 Subject: [PATCH 020/100] Bump go.uber.org/ratelimit from 0.2.0 to 0.3.1 in /src (#20204) Bumps [go.uber.org/ratelimit](https://github.com/uber-go/ratelimit) from 0.2.0 to 0.3.1. - [Changelog](https://github.com/uber-go/ratelimit/blob/main/CHANGELOG.md) - [Commits](https://github.com/uber-go/ratelimit/compare/v0.2.0...v0.3.1) --- updated-dependencies: - dependency-name: go.uber.org/ratelimit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Shengwen YU Co-authored-by: Wang Yan --- src/go.mod | 4 ++-- src/go.sum | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/go.mod b/src/go.mod index e25f95586..9960d620e 100644 --- a/src/go.mod +++ b/src/go.mod @@ -62,7 +62,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 go.opentelemetry.io/otel/sdk v1.24.0 go.opentelemetry.io/otel/trace v1.24.0 - go.uber.org/ratelimit v0.2.0 + go.uber.org/ratelimit v0.3.1 golang.org/x/crypto v0.21.0 golang.org/x/net v0.22.0 golang.org/x/oauth2 v0.19.0 @@ -93,7 +93,7 @@ require ( github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Unknwon/goconfig v0.0.0-20160216183935-5f601ca6ef4d // indirect - github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect + github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect diff --git a/src/go.sum b/src/go.sum index 3d452134c..077b14bcf 100644 --- a/src/go.sum +++ b/src/go.sum @@ -68,8 +68,6 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97 h1:bNE5ID4C3YOkROfvBjXJUG53gyb+8az3TQN02LqnGBk= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190726115642-cd293c93fd97/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= -github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -85,8 +83,9 @@ github.com/beego/beego/v2 v2.0.6 h1:21Aqz3+RzUE1yP9a5xdU6LK54n9Z7NLEJtR4PE7NrPQ= github.com/beego/beego/v2 v2.0.6/go.mod h1:CH2/JIaB4ceGYVQlYqTAFft4pVk/ol1ZkakUrUvAyns= github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0 h1:fQaDnUQvBXHHQdGBu9hz8nPznB4BeiPQokvmQVjmNEw= github.com/beego/i18n v0.0.0-20140604031826-e87155e8f0c0/go.mod h1:KLeFCpAMq2+50NkXC8iiJxLLiiTfTqrGtKEVm+2fk7s= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -678,8 +677,8 @@ go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/ratelimit v0.2.0 h1:UQE2Bgi7p2B85uP5dC2bbRtig0C+OeNRnNEafLjsLPA= -go.uber.org/ratelimit v0.2.0/go.mod h1:YYBV4e4naJvhpitQrWJu1vCpgB7CboMe0qhltKt6mUg= +go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= +go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= From 91efec1e2a4b1d434aa09d9472da9c1e292f0ab3 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Tue, 16 Apr 2024 11:11:59 +0800 Subject: [PATCH 021/100] fix: update the image reference format for audit log when pulling image (#20278) Signed-off-by: Shengwen Yu --- src/controller/event/topic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controller/event/topic.go b/src/controller/event/topic.go index 3514bb9dc..d099a8dbb 100644 --- a/src/controller/event/topic.go +++ b/src/controller/event/topic.go @@ -188,7 +188,7 @@ func (p *PullArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) { ResourceType: "artifact"} if len(p.Tags) == 0 { - auditLog.Resource = fmt.Sprintf("%s:%s", + auditLog.Resource = fmt.Sprintf("%s@%s", p.Artifact.RepositoryName, p.Artifact.Digest) } else { auditLog.Resource = fmt.Sprintf("%s:%s", From 550bf1d7509860e9628dcf52bb5e83e6d552269c Mon Sep 17 00:00:00 2001 From: Wang Yan Date: Tue, 16 Apr 2024 16:49:52 +0800 Subject: [PATCH 022/100] fix issue 20269 (#20274) By default, use the nvd score as the primary score, and if it is unavailable, fallback to the redhat score. fix #20269 Signed-off-by: wang yan --- .../scan/postprocessors/report_converters.go | 27 ++++++++++--------- .../postprocessors/report_converters_test.go | 2 ++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/pkg/scan/postprocessors/report_converters.go b/src/pkg/scan/postprocessors/report_converters.go index 538b75ce7..0a91f41ad 100644 --- a/src/pkg/scan/postprocessors/report_converters.go +++ b/src/pkg/scan/postprocessors/report_converters.go @@ -354,25 +354,28 @@ func (c *nativeToRelationalSchemaConverter) updateReport(ctx context.Context, vu return report.Mgr.Update(ctx, r, "CriticalCnt", "HighCnt", "MediumCnt", "LowCnt", "NoneCnt", "UnknownCnt", "FixableCnt") } -// CVSS ... -type CVSS struct { - NVD Nvd `json:"nvd"` +// CVS ... +type CVS struct { + CVSS map[string]map[string]interface{} `json:"CVSS"` } -// Nvd ... -type Nvd struct { - V3Score float64 `json:"V3Score"` -} - -func parseScoreFromVendorAttribute(ctx context.Context, vendorAttribute string) (NvdV3Score float64) { - var data map[string]CVSS +func parseScoreFromVendorAttribute(ctx context.Context, vendorAttribute string) float64 { + var data CVS err := json.Unmarshal([]byte(vendorAttribute), &data) if err != nil { log.G(ctx).Errorf("failed to parse vendor_attribute, error %v", err) return 0 } - if cvss, ok := data["CVSS"]; ok { - return cvss.NVD.V3Score + + // set the nvd as the first priority, if it's unavailable, return the first V3Score available. + if val, ok := data.CVSS["nvd"]["V3Score"]; ok { + return val.(float64) + } + + for vendor := range data.CVSS { + if val, ok := data.CVSS[vendor]["V3Score"]; ok { + return val.(float64) + } } return 0 } diff --git a/src/pkg/scan/postprocessors/report_converters_test.go b/src/pkg/scan/postprocessors/report_converters_test.go index 057113a47..32b7660fa 100644 --- a/src/pkg/scan/postprocessors/report_converters_test.go +++ b/src/pkg/scan/postprocessors/report_converters_test.go @@ -578,6 +578,8 @@ func Test_parseScoreFromVendorAttribute(t *testing.T) { {"both", args{`{"CVSS":{"nvd":{"V3Score":5.5,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H"},"redhat":{"V3Score":6.2,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}}}`}, 5.5}, {"both2", args{`{"CVSS":{"nvd":{"V2Score":7.2,"V2Vector":"AV:L/AC:L/Au:N/C:C/I:C/A:C","V3Score":7.8,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"},"redhat":{"V3Score":7.8,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 7.8}, {"none", args{`{"CVSS":{"nvd":{"V2Score":7.2,"V2Vector":"AV:L/AC:L/Au:N/C:C/I:C/A:C","V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"},"redhat":{"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 0}, + {"redhatonly", args{`{"CVSS":{"redhat":{"V3Score":8.8, "V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 8.8}, + {"nvdnov3butredhat", args{`{"CVSS":{"nvd":{"V2Score":7.2,"V2Vector":"AV:L/AC:L/Au:N/C:C/I:C/A:C"},"redhat":{"V3Score":7.8,"V3Vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H"}}}`}, 7.8}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 67c03ddc4f0a2d10b876a3d71f07886ee934f2a6 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Tue, 16 Apr 2024 18:37:16 +0800 Subject: [PATCH 023/100] fix: update TRIVYVERSION=v0.50.1 && TRIVYADAPTERVERSION=v0.31.0 (#20285) Signed-off-by: Shengwen Yu --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index e9dd6f7dd..7b86db516 100644 --- a/Makefile +++ b/Makefile @@ -104,8 +104,8 @@ PREPARE_VERSION_NAME=versions #versions REGISTRYVERSION=v2.8.3-patch-redis -TRIVYVERSION=v0.49.1 -TRIVYADAPTERVERSION=v0.30.22 +TRIVYVERSION=v0.50.1 +TRIVYADAPTERVERSION=v0.31.0 # version of registry for pulling the source code REGISTRY_SRC_TAG=v2.8.3 From 654aa8edcfad6007b1d15ac323c37bc2c140418c Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Tue, 16 Apr 2024 21:34:19 +0800 Subject: [PATCH 024/100] Add generate SBOM feature (#20251) * Add SBOM scan feature Add scan handler for sbom Delete previous sbom accessory before the job service Signed-off-by: stonezdj * fix issue Signed-off-by: stonezdj --------- Signed-off-by: stonezdj Signed-off-by: stonezdj Co-authored-by: stonezdj --- api/v2.0/swagger.yaml | 15 +- src/common/rbac/const.go | 5 + src/common/rbac/project/rbac_role.go | 9 + src/controller/artifact/controller.go | 2 + .../artifact/processor/sbom/sbom.go | 6 +- .../artifact/processor/sbom/sbom_test.go | 2 +- src/controller/scan/base_controller.go | 70 +++++++- src/controller/scan/base_controller_test.go | 32 +++- src/controller/scanner/base_controller.go | 23 ++- src/core/main.go | 1 + src/jobservice/main.go | 1 + src/pkg/scan/dao/scanner/model.go | 20 ++- src/pkg/scan/handler.go | 7 + src/pkg/scan/job.go | 29 +++- src/pkg/scan/job_test.go | 5 +- src/pkg/scan/rest/v1/client.go | 13 +- src/pkg/scan/rest/v1/client_test.go | 6 +- src/pkg/scan/rest/v1/models.go | 19 ++ src/pkg/scan/sbom/model/summary.go | 43 +++++ src/pkg/scan/sbom/sbom.go | 162 ++++++++++++++++++ src/pkg/scan/sbom/sbom_test.go | 139 +++++++++++++++ src/pkg/scan/vulnerability/vul.go | 15 ++ src/pkg/scan/vulnerability/vul_test.go | 64 +++++++ src/server/v2.0/handler/model/scanner.go | 1 + src/server/v2.0/handler/project.go | 8 +- src/server/v2.0/handler/project_test.go | 14 +- src/server/v2.0/handler/scan.go | 37 ++-- src/testing/pkg/scan/rest/v1/client.go | 18 +- 28 files changed, 697 insertions(+), 69 deletions(-) create mode 100644 src/pkg/scan/sbom/model/summary.go create mode 100644 src/pkg/scan/sbom/sbom.go create mode 100644 src/pkg/scan/sbom/sbom_test.go diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index ce4210ec8..0f5c18e73 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -996,7 +996,7 @@ paths: description: Specify whether the SBOM overview is included in returning artifacts, when this option is true, the SBOM overview will be included in the response type: boolean required: false - default: false + default: false - name: with_signature in: query description: Specify whether the signature is included inside the tags of the returning artifacts. Only works when setting "with_tag=true" @@ -1179,7 +1179,7 @@ paths: - name: scan_request_type in: body required: false - schema: + schema: $ref: '#/definitions/ScanRequestType' responses: '202': @@ -6769,7 +6769,7 @@ definitions: ScanRequestType: type: object properties: - scan_type: + scan_type: type: string description: 'The scan type for the scan request. Two options are currently supported, vulnerability and sbom' enum: [vulnerability, sbom] @@ -6797,12 +6797,12 @@ definitions: description: 'The status of the generating SBOM task' sbom_digest: type: string - description: 'The digest of the generated SBOM accessory' + description: 'The digest of the generated SBOM accessory' report_id: type: string description: 'id of the native scan report' - example: '5f62c830-f996-11e9-957f-0242c0a89008' - duration: + example: '5f62c830-f996-11e9-957f-0242c0a89008' + duration: type: integer format: int64 description: 'Time in seconds required to create the report' @@ -8437,7 +8437,7 @@ definitions: description: Indicates the capabilities of the scanner, e.g. support_vulnerability or support_sbom. additionalProperties: True example: {"support_vulnerability": true, "support_sbom": true} - + ScannerRegistrationReq: type: object required: @@ -9986,7 +9986,6 @@ definitions: items: type: string description: Links of the vulnerability - ScanType: type: object properties: diff --git a/src/common/rbac/const.go b/src/common/rbac/const.go index ff49ec3fd..a783e71d4 100644 --- a/src/common/rbac/const.go +++ b/src/common/rbac/const.go @@ -51,6 +51,7 @@ const ( ResourceRobot = Resource("robot") ResourceNotificationPolicy = Resource("notification-policy") ResourceScan = Resource("scan") + ResourceSBOM = Resource("sbom") ResourceScanner = Resource("scanner") ResourceArtifact = Resource("artifact") ResourceTag = Resource("tag") @@ -182,6 +183,10 @@ var ( {Resource: ResourceScan, Action: ActionRead}, {Resource: ResourceScan, Action: ActionStop}, + {Resource: ResourceSBOM, Action: ActionCreate}, + {Resource: ResourceSBOM, Action: ActionStop}, + {Resource: ResourceSBOM, Action: ActionRead}, + {Resource: ResourceTag, Action: ActionCreate}, {Resource: ResourceTag, Action: ActionList}, {Resource: ResourceTag, Action: ActionDelete}, diff --git a/src/common/rbac/project/rbac_role.go b/src/common/rbac/project/rbac_role.go index fa618b982..5ef773e9a 100644 --- a/src/common/rbac/project/rbac_role.go +++ b/src/common/rbac/project/rbac_role.go @@ -86,6 +86,9 @@ var ( {Resource: rbac.ResourceScan, Action: rbac.ActionCreate}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionStop}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionCreate}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionStop}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionCreate}, @@ -169,6 +172,9 @@ var ( {Resource: rbac.ResourceScan, Action: rbac.ActionCreate}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionStop}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionCreate}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionStop}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, @@ -223,6 +229,7 @@ var ( {Resource: rbac.ResourceRobot, Action: rbac.ActionList}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, @@ -267,6 +274,7 @@ var ( {Resource: rbac.ResourceRobot, Action: rbac.ActionList}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, @@ -290,6 +298,7 @@ var ( {Resource: rbac.ResourceConfiguration, Action: rbac.ActionRead}, {Resource: rbac.ResourceScan, Action: rbac.ActionRead}, + {Resource: rbac.ResourceSBOM, Action: rbac.ActionRead}, {Resource: rbac.ResourceScanner, Action: rbac.ActionRead}, diff --git a/src/controller/artifact/controller.go b/src/controller/artifact/controller.go index cc100211f..34ea29077 100644 --- a/src/controller/artifact/controller.go +++ b/src/controller/artifact/controller.go @@ -29,6 +29,7 @@ import ( "github.com/goharbor/harbor/src/controller/artifact/processor/chart" "github.com/goharbor/harbor/src/controller/artifact/processor/cnab" "github.com/goharbor/harbor/src/controller/artifact/processor/image" + "github.com/goharbor/harbor/src/controller/artifact/processor/sbom" "github.com/goharbor/harbor/src/controller/artifact/processor/wasm" "github.com/goharbor/harbor/src/controller/event/metadata" "github.com/goharbor/harbor/src/controller/tag" @@ -73,6 +74,7 @@ var ( chart.ArtifactTypeChart: icon.DigestOfIconChart, cnab.ArtifactTypeCNAB: icon.DigestOfIconCNAB, wasm.ArtifactTypeWASM: icon.DigestOfIconWASM, + sbom.ArtifactTypeSBOM: icon.DigestOfIconAccSBOM, } ) diff --git a/src/controller/artifact/processor/sbom/sbom.go b/src/controller/artifact/processor/sbom/sbom.go index ec0222fb9..4eb11f4bd 100644 --- a/src/controller/artifact/processor/sbom/sbom.go +++ b/src/controller/artifact/processor/sbom/sbom.go @@ -29,8 +29,8 @@ import ( ) const ( - // processorArtifactTypeSBOM is the artifact type for SBOM, it's scope is only used in the processor - processorArtifactTypeSBOM = "SBOM" + // ArtifactTypeSBOM is the artifact type for SBOM, it's scope is only used in the processor + ArtifactTypeSBOM = "SBOM" // processorMediaType is the media type for SBOM, it's scope is only used to register the processor processorMediaType = "application/vnd.goharbor.harbor.sbom.v1" ) @@ -85,5 +85,5 @@ func (m *Processor) AbstractAddition(_ context.Context, art *artifact.Artifact, // GetArtifactType the artifact type is used to display the artifact type in the UI func (m *Processor) GetArtifactType(_ context.Context, _ *artifact.Artifact) string { - return processorArtifactTypeSBOM + return ArtifactTypeSBOM } diff --git a/src/controller/artifact/processor/sbom/sbom_test.go b/src/controller/artifact/processor/sbom/sbom_test.go index 6128c550f..33889591a 100644 --- a/src/controller/artifact/processor/sbom/sbom_test.go +++ b/src/controller/artifact/processor/sbom/sbom_test.go @@ -158,7 +158,7 @@ func (suite *SBOMProcessorTestSuite) TestAbstractAdditionPullManifestError() { } func (suite *SBOMProcessorTestSuite) TestGetArtifactType() { - suite.Equal(processorArtifactTypeSBOM, suite.processor.GetArtifactType(context.Background(), &artifact.Artifact{})) + suite.Equal(ArtifactTypeSBOM, suite.processor.GetArtifactType(context.Background(), &artifact.Artifact{})) } func TestSBOMProcessorTestSuite(t *testing.T) { diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index a1627af30..be168098a 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -49,8 +49,10 @@ import ( "github.com/goharbor/harbor/src/pkg/scan/postprocessors" "github.com/goharbor/harbor/src/pkg/scan/report" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + sbomModel "github.com/goharbor/harbor/src/pkg/scan/sbom/model" "github.com/goharbor/harbor/src/pkg/scan/vuln" "github.com/goharbor/harbor/src/pkg/task" + "github.com/goharbor/harbor/src/testing/controller/artifact" ) var ( @@ -108,6 +110,8 @@ type basicController struct { rc robot.Controller // Tag controller tagCtl tag.Controller + // Artifact controller + artCtl artifact.Controller // UUID generator uuid uuidGenerator // Configuration getter func @@ -259,7 +263,7 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti launchScanJobParams []*launchScanJobParam ) for _, art := range artifacts { - reports, err := bc.makeReportPlaceholder(ctx, r, art) + reports, err := bc.makeReportPlaceholder(ctx, r, art, opts) if err != nil { if errors.IsConflictErr(err) { errs = append(errs, err) @@ -326,7 +330,7 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti for _, launchScanJobParam := range launchScanJobParams { launchScanJobParam.ExecutionID = opts.ExecutionID - if err := bc.launchScanJob(ctx, launchScanJobParam); err != nil { + if err := bc.launchScanJob(ctx, launchScanJobParam, opts); err != nil { log.G(ctx).Warningf("scan artifact %s@%s failed, error: %v", artifact.RepositoryName, artifact.Digest, err) errs = append(errs, err) } @@ -546,13 +550,15 @@ func (bc *basicController) startScanAll(ctx context.Context, executionID int64) return nil } -func (bc *basicController) makeReportPlaceholder(ctx context.Context, r *scanner.Registration, art *ar.Artifact) ([]*scan.Report, error) { - mimeTypes := r.GetProducesMimeTypes(art.ManifestMediaType) - +func (bc *basicController) makeReportPlaceholder(ctx context.Context, r *scanner.Registration, art *ar.Artifact, opts *Options) ([]*scan.Report, error) { + mimeTypes := r.GetProducesMimeTypes(art.ManifestMediaType, opts.GetScanType()) oldReports, err := bc.manager.GetBy(bc.cloneCtx(ctx), art.Digest, r.UUID, mimeTypes) if err != nil { return nil, err } + if err := bc.deleteArtifactAccessories(ctx, oldReports); err != nil { + return nil, err + } if err := bc.assembleReports(ctx, oldReports...); err != nil { return nil, err @@ -574,7 +580,7 @@ func (bc *basicController) makeReportPlaceholder(ctx context.Context, r *scanner var reports []*scan.Report - for _, pm := range r.GetProducesMimeTypes(art.ManifestMediaType) { + for _, pm := range r.GetProducesMimeTypes(art.ManifestMediaType, opts.GetScanType()) { report := &scan.Report{ Digest: art.Digest, RegistrationUUID: r.UUID, @@ -991,7 +997,7 @@ func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64 } // launchScanJob launches a job to run scan -func (bc *basicController) launchScanJob(ctx context.Context, param *launchScanJobParam) error { +func (bc *basicController) launchScanJob(ctx context.Context, param *launchScanJobParam, opts *Options) error { // don't launch scan job for the artifact which is not supported by the scanner if !hasCapability(param.Registration, param.Artifact) { return nil @@ -1032,6 +1038,11 @@ func (bc *basicController) launchScanJob(ctx context.Context, param *launchScanJ MimeType: param.Artifact.ManifestMediaType, Size: param.Artifact.Size, }, + RequestType: []*v1.ScanType{ + { + Type: opts.GetScanType(), + }, + }, } rJSON, err := param.Registration.ToJSON() @@ -1265,3 +1276,48 @@ func parseOptions(options ...Option) (*Options, error) { return ops, nil } + +// deleteArtifactAccessories delete the accessory in reports, only delete sbom accessory +func (bc *basicController) deleteArtifactAccessories(ctx context.Context, reports []*scan.Report) error { + for _, rpt := range reports { + if rpt.MimeType != v1.MimeTypeSBOMReport { + continue + } + if err := bc.deleteArtifactAccessory(ctx, rpt.Report); err != nil { + return err + } + } + return nil +} + +// deleteArtifactAccessory check if current report has accessory info, if there is, delete it +func (bc *basicController) deleteArtifactAccessory(ctx context.Context, report string) error { + if len(report) == 0 { + return nil + } + sbomSummary := sbomModel.Summary{} + if err := json.Unmarshal([]byte(report), &sbomSummary); err != nil { + // it could be a non sbom report, just skip + log.Debugf("fail to unmarshal %v, skip to delete sbom report", err) + return nil + } + repo, dgst := sbomSummary.SBOMAccArt() + if len(repo) == 0 || len(dgst) == 0 { + return nil + } + art, err := bc.ar.GetByReference(ctx, repo, dgst, nil) + if err != nil { + if errors.IsNotFoundErr(err) { + return nil + } + return err + } + if art == nil { + return nil + } + err = bc.ar.Delete(ctx, art.ID) + if errors.IsNotFoundErr(err) { + return nil + } + return err +} diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index 9d485f16c..521325d79 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -108,6 +108,7 @@ func (suite *ControllerTestSuite) SetupSuite() { Version: "0.1.0", }, Capabilities: []*v1.ScannerCapability{{ + Type: v1.ScanTypeVulnerability, ConsumesMimeTypes: []string{ v1.MimeTypeOCIArtifact, v1.MimeTypeDockerArtifact, @@ -115,7 +116,17 @@ func (suite *ControllerTestSuite) SetupSuite() { ProducesMimeTypes: []string{ v1.MimeTypeNativeReport, }, - }}, + }, + { + Type: v1.ScanTypeSbom, + ConsumesMimeTypes: []string{ + v1.MimeTypeOCIArtifact, + }, + ProducesMimeTypes: []string{ + v1.MimeTypeSBOMReport, + }, + }, + }, Properties: v1.ScannerProperties{ "extra": "testing", }, @@ -655,3 +666,22 @@ func TestIsSBOMMimeTypes(t *testing.T) { // Test with an empty slice assert.False(t, isSBOMMimeTypes([]string{})) } + +func (suite *ControllerTestSuite) TestDeleteArtifactAccessories() { + // artifact not provided + suite.Nil(suite.c.deleteArtifactAccessories(context.TODO(), nil)) + + // artifact is provided + art := &artifact.Artifact{Artifact: art.Artifact{ID: 1, ProjectID: 1, RepositoryName: "library/photon"}} + mock.OnAnything(suite.ar, "GetByReference").Return(art, nil).Once() + mock.OnAnything(suite.ar, "Delete").Return(nil).Once() + reportContent := `{"sbom_digest":"sha256:12345", "scan_status":"Success", "duration":3, "sbom_repository":"library/photon"}` + emptyReportContent := `` + reports := []*scan.Report{ + {Report: reportContent}, + {Report: emptyReportContent}, + } + ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) + suite.NoError(suite.c.deleteArtifactAccessories(ctx, reports)) + +} diff --git a/src/controller/scanner/base_controller.go b/src/controller/scanner/base_controller.go index 2028da355..068424a1e 100644 --- a/src/controller/scanner/base_controller.go +++ b/src/controller/scanner/base_controller.go @@ -79,7 +79,11 @@ func (bc *basicController) ListRegistrations(ctx context.Context, query *q.Query if err != nil { return nil, errors.Wrap(err, "api controller: list registrations") } - + for _, r := range l { + if err := bc.appendCap(ctx, r); err != nil { + return nil, err + } + } return l, nil } @@ -122,10 +126,25 @@ func (bc *basicController) GetRegistration(ctx context.Context, registrationUUID if err != nil { return nil, errors.Wrap(err, "api controller: get registration") } - + if r == nil { + return nil, nil + } + if err := bc.appendCap(ctx, r); err != nil { + return nil, err + } return r, nil } +func (bc *basicController) appendCap(ctx context.Context, r *scanner.Registration) error { + mt, err := bc.Ping(ctx, r) + if err != nil { + logger.Errorf("Get registration error: %s", err) + return err + } + r.Capabilities = mt.ConvertCapability() + return nil +} + // RegistrationExists ... func (bc *basicController) RegistrationExists(ctx context.Context, registrationUUID string) bool { registration, err := bc.manager.Get(ctx, registrationUUID) diff --git a/src/core/main.go b/src/core/main.go index ebc786d7e..f0bc96564 100644 --- a/src/core/main.go +++ b/src/core/main.go @@ -70,6 +70,7 @@ import ( "github.com/goharbor/harbor/src/pkg/oidc" "github.com/goharbor/harbor/src/pkg/scan" "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" + _ "github.com/goharbor/harbor/src/pkg/scan/sbom" _ "github.com/goharbor/harbor/src/pkg/scan/vulnerability" pkguser "github.com/goharbor/harbor/src/pkg/user" "github.com/goharbor/harbor/src/pkg/version" diff --git a/src/jobservice/main.go b/src/jobservice/main.go index f288efea7..e00dd4b20 100644 --- a/src/jobservice/main.go +++ b/src/jobservice/main.go @@ -36,6 +36,7 @@ import ( _ "github.com/goharbor/harbor/src/pkg/accessory/model/subject" _ "github.com/goharbor/harbor/src/pkg/config/inmemory" _ "github.com/goharbor/harbor/src/pkg/config/rest" + _ "github.com/goharbor/harbor/src/pkg/scan/sbom" _ "github.com/goharbor/harbor/src/pkg/scan/vulnerability" ) diff --git a/src/pkg/scan/dao/scanner/model.go b/src/pkg/scan/dao/scanner/model.go index dbdcf8b1d..cc418e624 100644 --- a/src/pkg/scan/dao/scanner/model.go +++ b/src/pkg/scan/dao/scanner/model.go @@ -66,8 +66,9 @@ type Registration struct { Metadata *v1.ScannerAdapterMetadata `orm:"-" json:"-"` // Timestamps - CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` - UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + CreateTime time.Time `orm:"column(create_time);auto_now_add;type(datetime)" json:"create_time"` + UpdateTime time.Time `orm:"column(update_time);auto_now;type(datetime)" json:"update_time"` + Capabilities map[string]interface{} `orm:"-" json:"capabilities,omitempty"` } // TableName for Endpoint @@ -151,15 +152,20 @@ func (r *Registration) HasCapability(manifestMimeType string) bool { } // GetProducesMimeTypes returns produces mime types for the artifact -func (r *Registration) GetProducesMimeTypes(mimeType string) []string { +func (r *Registration) GetProducesMimeTypes(mimeType string, scanType string) []string { if r.Metadata == nil { return nil } - for _, capability := range r.Metadata.Capabilities { - for _, mt := range capability.ConsumesMimeTypes { - if mt == mimeType { - return capability.ProducesMimeTypes + capType := capability.Type + if len(capType) == 0 { + capType = v1.ScanTypeVulnerability + } + if scanType == capType { + for _, mt := range capability.ConsumesMimeTypes { + if mt == mimeType { + return capability.ProducesMimeTypes + } } } } diff --git a/src/pkg/scan/handler.go b/src/pkg/scan/handler.go index 7ddba595d..f402107c7 100644 --- a/src/pkg/scan/handler.go +++ b/src/pkg/scan/handler.go @@ -38,7 +38,14 @@ func GetScanHandler(requestType string) Handler { // Handler handler for scan job, it could be implement by different scan type, such as vulnerability, sbom type Handler interface { + // RequestProducesMineTypes returns the produces mime types + RequestProducesMineTypes() []string + // RequiredPermissions defines the permission used by the scan robot account RequiredPermissions() []*types.Policy + // RequestParameters defines the parameters for scan request + RequestParameters() map[string]interface{} + // ReportURLParameter defines the parameters for scan report + ReportURLParameter(sr *v1.ScanRequest) (string, error) // PostScan defines the operation after scan PostScan(ctx job.Context, sr *v1.ScanRequest, rp *scan.Report, rawReport string, startTime time.Time, robot *model.Robot) (string, error) } diff --git a/src/pkg/scan/job.go b/src/pkg/scan/job.go index c21b48cb2..171e0c307 100644 --- a/src/pkg/scan/job.go +++ b/src/pkg/scan/job.go @@ -242,7 +242,13 @@ func (j *Job) Run(ctx job.Context, params job.Parameters) error { } myLogger.Debugf("check scan report for mime %s at %s", m, t.Format("2006/01/02 15:04:05")) - rawReport, err := fetchScanReportFromScanner(client, resp.ID, m) + + reportURLParameter, err := handler.ReportURLParameter(req) + if err != nil { + errs[i] = errors.Wrap(err, "scan job: get report url") + return + } + rawReport, err := fetchScanReportFromScanner(client, resp.ID, m, reportURLParameter) if err != nil { // Not ready yet if notReadyErr, ok := err.(*v1.ReportNotReadyError); ok { @@ -332,13 +338,13 @@ func getReportPlaceholder(ctx context.Context, digest string, reportUUID string, return reports[0], nil } -func fetchScanReportFromScanner(client v1.Client, requestID string, m string) (rawReport string, err error) { - rawReport, err = client.GetScanReport(requestID, m) +func fetchScanReportFromScanner(client v1.Client, requestID string, mimType string, urlParameter string) (rawReport string, err error) { + rawReport, err = client.GetScanReport(requestID, mimType, urlParameter) if err != nil { return "", err } // Make sure the data is aligned with the v1 spec. - if _, err = report.ResolveData(m, []byte(rawReport)); err != nil { + if _, err = report.ResolveData(mimType, []byte(rawReport)); err != nil { return "", err } return rawReport, nil @@ -367,7 +373,20 @@ func ExtractScanReq(params job.Parameters) (*v1.ScanRequest, error) { if err := req.Validate(); err != nil { return nil, err } - + reqType := v1.ScanTypeVulnerability + // attach the request with ProducesMimeTypes and Parameters + if len(req.RequestType) > 0 { + // current only support requestType with one element for each request + if len(req.RequestType[0].Type) > 0 { + reqType = req.RequestType[0].Type + } + handler := GetScanHandler(reqType) + if handler == nil { + return nil, errors.Errorf("failed to get scan handler, request type %v", reqType) + } + req.RequestType[0].ProducesMimeTypes = handler.RequestProducesMineTypes() + req.RequestType[0].Parameters = handler.RequestParameters() + } return req, nil } diff --git a/src/pkg/scan/job_test.go b/src/pkg/scan/job_test.go index 92571285d..ff00dd20f 100644 --- a/src/pkg/scan/job_test.go +++ b/src/pkg/scan/job_test.go @@ -211,8 +211,9 @@ func (suite *JobTestSuite) TestfetchScanReportFromScanner() { suite.reportIDs = append(suite.reportIDs, rptID) require.NoError(suite.T(), err) client := &v1testing.Client{} - client.On("GetScanReport", mock.Anything, v1.MimeTypeGenericVulnerabilityReport).Return(rawContent, nil) - rawRept, err := fetchScanReportFromScanner(client, "abc", v1.MimeTypeGenericVulnerabilityReport) + client.On("GetScanReport", mock.Anything, v1.MimeTypeGenericVulnerabilityReport, mock.Anything).Return(rawContent, nil) + parameters := "sbom_media_type=application/spdx+json" + rawRept, err := fetchScanReportFromScanner(client, "abc", v1.MimeTypeGenericVulnerabilityReport, parameters) require.NoError(suite.T(), err) require.Equal(suite.T(), rawContent, rawRept) } diff --git a/src/pkg/scan/rest/v1/client.go b/src/pkg/scan/rest/v1/client.go index 251ccef1f..a5ae04075 100644 --- a/src/pkg/scan/rest/v1/client.go +++ b/src/pkg/scan/rest/v1/client.go @@ -68,7 +68,7 @@ type Client interface { // Returns: // string : the scan report of the given artifact // error : non nil error if any errors occurred - GetScanReport(scanRequestID, reportMIMEType string) (string, error) + GetScanReport(scanRequestID, reportMIMEType string, urlParameter string) (string, error) } // basicClient is default implementation of the Client interface @@ -97,7 +97,7 @@ func NewClient(url, authType, accessCredential string, skipCertVerify bool) (Cli httpClient: &http.Client{ Timeout: time.Second * 5, Transport: transport, - CheckRedirect: func(req *http.Request, via []*http.Request) error { + CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }, }, @@ -167,7 +167,7 @@ func (c *basicClient) SubmitScan(req *ScanRequest) (*ScanResponse, error) { } // GetScanReport ... -func (c *basicClient) GetScanReport(scanRequestID, reportMIMEType string) (string, error) { +func (c *basicClient) GetScanReport(scanRequestID, reportMIMEType string, urlParameter string) (string, error) { if len(scanRequestID) == 0 { return "", errors.New("empty scan request ID") } @@ -177,8 +177,11 @@ func (c *basicClient) GetScanReport(scanRequestID, reportMIMEType string) (strin } def := c.spec.GetScanReport(scanRequestID, reportMIMEType) - - req, err := http.NewRequest(http.MethodGet, def.URL, nil) + reportURL := def.URL + if len(urlParameter) > 0 { + reportURL = fmt.Sprintf("%s?%s", def.URL, urlParameter) + } + req, err := http.NewRequest(http.MethodGet, reportURL, nil) if err != nil { return "", errors.Wrap(err, "v1 client: get scan report") } diff --git a/src/pkg/scan/rest/v1/client_test.go b/src/pkg/scan/rest/v1/client_test.go index ee3435066..5893514d6 100644 --- a/src/pkg/scan/rest/v1/client_test.go +++ b/src/pkg/scan/rest/v1/client_test.go @@ -72,7 +72,7 @@ func (suite *ClientTestSuite) TestClientSubmitScan() { // TestClientGetScanReportError tests getting report failed func (suite *ClientTestSuite) TestClientGetScanReportError() { - _, err := suite.client.GetScanReport("id1", MimeTypeNativeReport) + _, err := suite.client.GetScanReport("id1", MimeTypeNativeReport, "") require.Error(suite.T(), err) assert.Condition(suite.T(), func() (success bool) { success = strings.Index(err.Error(), "error") != -1 @@ -82,14 +82,14 @@ func (suite *ClientTestSuite) TestClientGetScanReportError() { // TestClientGetScanReport tests getting report func (suite *ClientTestSuite) TestClientGetScanReport() { - res, err := suite.client.GetScanReport("id2", MimeTypeNativeReport) + res, err := suite.client.GetScanReport("id2", MimeTypeNativeReport, "") require.NoError(suite.T(), err) require.NotEmpty(suite.T(), res) } // TestClientGetScanReportNotReady tests the case that the report is not ready func (suite *ClientTestSuite) TestClientGetScanReportNotReady() { - _, err := suite.client.GetScanReport("id3", MimeTypeNativeReport) + _, err := suite.client.GetScanReport("id3", MimeTypeNativeReport, "") require.Error(suite.T(), err) require.Condition(suite.T(), func() (success bool) { _, success = err.(*ReportNotReadyError) diff --git a/src/pkg/scan/rest/v1/models.go b/src/pkg/scan/rest/v1/models.go index c31edb93b..21352c749 100644 --- a/src/pkg/scan/rest/v1/models.go +++ b/src/pkg/scan/rest/v1/models.go @@ -21,6 +21,11 @@ import ( "github.com/goharbor/harbor/src/lib/errors" ) +const ( + supportVulnerability = "support_vulnerability" + supportSBOM = "support_sbom" +) + var supportedMimeTypes = []string{ MimeTypeNativeReport, MimeTypeGenericVulnerabilityReport, @@ -153,6 +158,20 @@ func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapabil return nil } +// ConvertCapability converts the capability to map, used in get scanner API +func (md *ScannerAdapterMetadata) ConvertCapability() map[string]interface{} { + capabilities := make(map[string]interface{}) + for _, c := range md.Capabilities { + if c.Type == ScanTypeVulnerability { + capabilities[supportVulnerability] = true + } + if c.Type == ScanTypeSbom { + capabilities[supportSBOM] = true + } + } + return capabilities +} + // Artifact represents an artifact stored in Registry. type Artifact struct { // ID of the namespace (project). It will not be sent to scanner adapter. diff --git a/src/pkg/scan/sbom/model/summary.go b/src/pkg/scan/sbom/model/summary.go new file mode 100644 index 000000000..46c870f97 --- /dev/null +++ b/src/pkg/scan/sbom/model/summary.go @@ -0,0 +1,43 @@ +// 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 + +const ( + // SBOMRepository ... + SBOMRepository = "sbom_repository" + // SBOMDigest ... + SBOMDigest = "sbom_digest" + // StartTime ... + StartTime = "start_time" + // EndTime ... + EndTime = "end_time" + // Duration ... + Duration = "duration" + // ScanStatus ... + ScanStatus = "scan_status" +) + +// Summary includes the sbom summary information +type Summary map[string]interface{} + +// SBOMAccArt returns the repository and digest of the SBOM +func (s Summary) SBOMAccArt() (repo, digest string) { + if repo, ok := s[SBOMRepository].(string); ok { + if digest, ok := s[SBOMDigest].(string); ok { + return repo, digest + } + } + return "", "" +} diff --git a/src/pkg/scan/sbom/sbom.go b/src/pkg/scan/sbom/sbom.go new file mode 100644 index 000000000..bbf405571 --- /dev/null +++ b/src/pkg/scan/sbom/sbom.go @@ -0,0 +1,162 @@ +// 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 sbom + +import ( + "context" + "encoding/json" + "fmt" + "net/url" + "strings" + "time" + + "github.com/goharbor/harbor/src/common" + "github.com/goharbor/harbor/src/lib/config" + scanModel "github.com/goharbor/harbor/src/pkg/scan/dao/scan" + sbom "github.com/goharbor/harbor/src/pkg/scan/sbom/model" + + "github.com/goharbor/harbor/src/common/rbac" + "github.com/goharbor/harbor/src/jobservice/job" + "github.com/goharbor/harbor/src/pkg/permission/types" + "github.com/goharbor/harbor/src/pkg/robot/model" + "github.com/goharbor/harbor/src/pkg/scan" + + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + "github.com/goharbor/harbor/src/pkg/scan/vuln" +) + +const ( + sbomMimeType = "application/vnd.goharbor.harbor.sbom.v1" + sbomMediaTypeSpdx = "application/spdx+json" +) + +func init() { + scan.RegisterScanHanlder(v1.ScanTypeSbom, &scanHandler{GenAccessoryFunc: scan.GenAccessoryArt, RegistryServer: registryFQDN}) +} + +// ScanHandler defines the Handler to generate sbom +type scanHandler struct { + GenAccessoryFunc func(scanRep v1.ScanRequest, sbomContent []byte, labels map[string]string, mediaType string, robot *model.Robot) (string, error) + RegistryServer func(ctx context.Context) string +} + +// RequestProducesMineTypes defines the mine types produced by the scan handler +func (v *scanHandler) RequestProducesMineTypes() []string { + return []string{v1.MimeTypeSBOMReport} +} + +// RequestParameters defines the parameters for scan request +func (v *scanHandler) RequestParameters() map[string]interface{} { + return map[string]interface{}{"sbom_media_types": []string{sbomMediaTypeSpdx}} +} + +// ReportURLParameter defines the parameters for scan report url +func (v *scanHandler) ReportURLParameter(_ *v1.ScanRequest) (string, error) { + return fmt.Sprintf("sbom_media_type=%s", url.QueryEscape(sbomMediaTypeSpdx)), nil +} + +// RequiredPermissions defines the permission used by the scan robot account +func (v *scanHandler) RequiredPermissions() []*types.Policy { + return []*types.Policy{ + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionScannerPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPush, + }, + } +} + +// PostScan defines task specific operations after the scan is complete +func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel.Report, rawReport string, startTime time.Time, robot *model.Robot) (string, error) { + sbomContent, err := retrieveSBOMContent(rawReport) + if err != nil { + return "", err + } + scanReq := v1.ScanRequest{ + Registry: sr.Registry, + Artifact: sr.Artifact, + } + // the registry server url is core by default, need to replace it with real registry server url + scanReq.Registry.URL = v.RegistryServer(ctx.SystemContext()) + if len(scanReq.Registry.URL) == 0 { + return "", fmt.Errorf("empty registry server") + } + myLogger := ctx.GetLogger() + myLogger.Debugf("Pushing accessory artifact to %s/%s", scanReq.Registry.URL, scanReq.Artifact.Repository) + dgst, err := v.GenAccessoryFunc(scanReq, sbomContent, v.annotations(), sbomMimeType, robot) + if err != nil { + myLogger.Errorf("error when create accessory from image %v", err) + return "", err + } + return v.generateReport(startTime, sr.Artifact.Repository, dgst, "Success") +} + +// annotations defines the annotations for the accessory artifact +func (v *scanHandler) annotations() map[string]string { + return map[string]string{ + "created-by": "Harbor", + "org.opencontainers.artifact.created": time.Now().Format(time.RFC3339), + "org.opencontainers.artifact.description": "SPDX JSON SBOM", + } +} + +func (v *scanHandler) generateReport(startTime time.Time, repository, digest, status string) (string, error) { + summary := sbom.Summary{} + endTime := time.Now() + summary[sbom.StartTime] = startTime + summary[sbom.EndTime] = endTime + summary[sbom.Duration] = int64(endTime.Sub(startTime).Seconds()) + summary[sbom.SBOMRepository] = repository + summary[sbom.SBOMDigest] = digest + summary[sbom.ScanStatus] = status + rep, err := json.Marshal(summary) + if err != nil { + return "", err + } + return string(rep), nil +} + +// extract server name from config, and remove the protocol prefix +func registryFQDN(ctx context.Context) string { + cfgMgr, ok := config.FromContext(ctx) + if ok { + extURL := cfgMgr.Get(context.Background(), common.ExtEndpoint).GetString() + server := strings.TrimPrefix(extURL, "https://") + server = strings.TrimPrefix(server, "http://") + return server + } + return "" +} + +// retrieveSBOMContent retrieves the "sbom" field from the raw report +func retrieveSBOMContent(rawReport string) ([]byte, error) { + rpt := vuln.Report{} + err := json.Unmarshal([]byte(rawReport), &rpt) + if err != nil { + return nil, err + } + sbomContent, err := json.Marshal(rpt.SBOM) + if err != nil { + return nil, err + } + return sbomContent, nil +} diff --git a/src/pkg/scan/sbom/sbom_test.go b/src/pkg/scan/sbom/sbom_test.go new file mode 100644 index 000000000..cf56b3bbb --- /dev/null +++ b/src/pkg/scan/sbom/sbom_test.go @@ -0,0 +1,139 @@ +package sbom + +import ( + "context" + "reflect" + "testing" + "time" + + "github.com/goharbor/harbor/src/common/rbac" + "github.com/goharbor/harbor/src/pkg/permission/types" + "github.com/goharbor/harbor/src/pkg/robot/model" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + "github.com/goharbor/harbor/src/testing/jobservice" + + "github.com/stretchr/testify/suite" +) + +func Test_scanHandler_ReportURLParameter(t *testing.T) { + type args struct { + in0 *v1.ScanRequest + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + {"normal test", args{&v1.ScanRequest{}}, "sbom_media_type=application%2Fspdx%2Bjson", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &scanHandler{} + got, err := v.ReportURLParameter(tt.args.in0) + if (err != nil) != tt.wantErr { + t.Errorf("ReportURLParameter() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ReportURLParameter() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_scanHandler_RequiredPermissions(t *testing.T) { + tests := []struct { + name string + want []*types.Policy + }{ + {"normal test", []*types.Policy{ + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionScannerPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPush, + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &scanHandler{} + if got := v.RequiredPermissions(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("RequiredPermissions() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_scanHandler_RequestProducesMineTypes(t *testing.T) { + tests := []struct { + name string + want []string + }{ + {"normal test", []string{v1.MimeTypeSBOMReport}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &scanHandler{} + if got := v.RequestProducesMineTypes(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("RequestProducesMineTypes() = %v, want %v", got, tt.want) + } + }) + } +} + +func mockGetRegistry(ctx context.Context) string { + return "myharbor.example.com" +} + +func mockGenAccessory(scanRep v1.ScanRequest, sbomContent []byte, labels map[string]string, mediaType string, robot *model.Robot) (string, error) { + return "sha256:1234567890", nil +} + +type ExampleTestSuite struct { + handler *scanHandler + suite.Suite +} + +func (suite *ExampleTestSuite) SetupSuite() { + suite.handler = &scanHandler{ + GenAccessoryFunc: mockGenAccessory, + RegistryServer: mockGetRegistry, + } +} + +func (suite *ExampleTestSuite) TearDownSuite() { +} + +func (suite *ExampleTestSuite) TestPostScan() { + req := &v1.ScanRequest{ + Registry: &v1.Registry{ + URL: "myregistry.example.com", + }, + Artifact: &v1.Artifact{ + Repository: "library/nosql", + }, + } + robot := &model.Robot{ + Name: "robot", + Secret: "mysecret", + } + startTime := time.Now() + rawReport := `{"sbom": { "key": "value" }}` + ctx := &jobservice.MockJobContext{} + ctx.On("GetLogger").Return(&jobservice.MockJobLogger{}) + accessory, err := suite.handler.PostScan(ctx, req, nil, rawReport, startTime, robot) + suite.Require().NoError(err) + suite.Require().NotEmpty(accessory) +} + +func TestExampleTestSuite(t *testing.T) { + suite.Run(t, &ExampleTestSuite{}) +} diff --git a/src/pkg/scan/vulnerability/vul.go b/src/pkg/scan/vulnerability/vul.go index 804659c09..2e9194c4a 100644 --- a/src/pkg/scan/vulnerability/vul.go +++ b/src/pkg/scan/vulnerability/vul.go @@ -35,6 +35,16 @@ func init() { type ScanHandler struct { } +// RequestProducesMineTypes returns the produces mime types +func (v *ScanHandler) RequestProducesMineTypes() []string { + return []string{v1.MimeTypeGenericVulnerabilityReport} +} + +// RequestParameters defines the parameters for scan request +func (v *ScanHandler) RequestParameters() map[string]interface{} { + return nil +} + // RequiredPermissions defines the permission used by the scan robot account func (v *ScanHandler) RequiredPermissions() []*types.Policy { return []*types.Policy{ @@ -49,6 +59,11 @@ func (v *ScanHandler) RequiredPermissions() []*types.Policy { } } +// ReportURLParameter vulnerability doesn't require any scan report parameters +func (v *ScanHandler) ReportURLParameter(_ *v1.ScanRequest) (string, error) { + return "", nil +} + // PostScan ... func (v *ScanHandler) PostScan(ctx job.Context, _ *v1.ScanRequest, origRp *scan.Report, rawReport string, _ time.Time, _ *model.Robot) (string, error) { // use a new ormer here to use the short db connection diff --git a/src/pkg/scan/vulnerability/vul_test.go b/src/pkg/scan/vulnerability/vul_test.go index 50d84287e..003e15a0d 100644 --- a/src/pkg/scan/vulnerability/vul_test.go +++ b/src/pkg/scan/vulnerability/vul_test.go @@ -1,6 +1,7 @@ package vulnerability import ( + "fmt" "testing" "time" @@ -50,3 +51,66 @@ func TestPostScan(t *testing.T) { assert.Equal(t, "", refreshedReport, "PostScan should return the refreshed report") assert.Nil(t, err, "PostScan should not return an error") } + +func TestScanHandler_RequiredPermissions(t *testing.T) { + tests := []struct { + name string + want []*types.Policy + }{ + {"normal", []*types.Policy{ + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionPull, + }, + { + Resource: rbac.ResourceRepository, + Action: rbac.ActionScannerPull, + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &ScanHandler{} + assert.Equalf(t, tt.want, v.RequiredPermissions(), "RequiredPermissions()") + }) + } +} + +func TestScanHandler_ReportURLParameter(t *testing.T) { + type args struct { + in0 *v1.ScanRequest + } + tests := []struct { + name string + args args + want string + wantErr assert.ErrorAssertionFunc + }{ + {"normal", args{&v1.ScanRequest{}}, "", assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &ScanHandler{} + got, err := v.ReportURLParameter(tt.args.in0) + if !tt.wantErr(t, err, fmt.Sprintf("ReportURLParameter(%v)", tt.args.in0)) { + return + } + assert.Equalf(t, tt.want, got, "ReportURLParameter(%v)", tt.args.in0) + }) + } +} + +func TestScanHandler_RequestProducesMineTypes(t *testing.T) { + tests := []struct { + name string + want []string + }{ + {"normal", []string{v1.MimeTypeGenericVulnerabilityReport}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &ScanHandler{} + assert.Equalf(t, tt.want, v.RequestProducesMineTypes(), "RequestProducesMineTypes()") + }) + } +} diff --git a/src/server/v2.0/handler/model/scanner.go b/src/server/v2.0/handler/model/scanner.go index bb140937f..a2fd45ce9 100644 --- a/src/server/v2.0/handler/model/scanner.go +++ b/src/server/v2.0/handler/model/scanner.go @@ -52,6 +52,7 @@ func (s *ScannerRegistration) ToSwagger(_ context.Context) *models.ScannerRegist Vendor: s.Vendor, Version: s.Version, Health: s.Health, + Capabilities: s.Capabilities, } } diff --git a/src/server/v2.0/handler/project.go b/src/server/v2.0/handler/project.go index 692a26d6f..f9848345f 100644 --- a/src/server/v2.0/handler/project.go +++ b/src/server/v2.0/handler/project.go @@ -594,7 +594,13 @@ func (a *projectAPI) GetScannerOfProject(ctx context.Context, params operation.G if err != nil { return a.SendError(ctx, err) } - + if scanner != nil { + metadata, err := a.scannerCtl.GetMetadata(ctx, scanner.UUID) + if err != nil { + return a.SendError(ctx, err) + } + scanner.Capabilities = metadata.ConvertCapability() + } return operation.NewGetScannerOfProjectOK().WithPayload(model.NewScannerRegistration(scanner).ToSwagger(ctx)) } diff --git a/src/server/v2.0/handler/project_test.go b/src/server/v2.0/handler/project_test.go index 21ba79a0a..9289829b8 100644 --- a/src/server/v2.0/handler/project_test.go +++ b/src/server/v2.0/handler/project_test.go @@ -22,6 +22,7 @@ import ( "github.com/goharbor/harbor/src/pkg/project/models" "github.com/goharbor/harbor/src/pkg/scan/dao/scanner" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" "github.com/goharbor/harbor/src/server/v2.0/restapi" projecttesting "github.com/goharbor/harbor/src/testing/controller/project" scannertesting "github.com/goharbor/harbor/src/testing/controller/scanner" @@ -36,6 +37,7 @@ type ProjectTestSuite struct { scannerCtl *scannertesting.Controller project *models.Project reg *scanner.Registration + metadata *v1.ScannerAdapterMetadata } func (suite *ProjectTestSuite) SetupSuite() { @@ -59,7 +61,12 @@ func (suite *ProjectTestSuite) SetupSuite() { scannerCtl: suite.scannerCtl, }, } - + suite.metadata = &v1.ScannerAdapterMetadata{ + Capabilities: []*v1.ScannerCapability{ + {Type: "vulnerability", ProducesMimeTypes: []string{v1.MimeTypeScanResponse}}, + {Type: "sbom", ProducesMimeTypes: []string{v1.MimeTypeSBOMReport}}, + }, + } suite.Suite.SetupSuite() } @@ -81,7 +88,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { // scanner not found mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(nil, nil).Once() - + mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() res, err := suite.Get("/projects/1/scanner") suite.NoError(err) suite.Equal(200, res.StatusCode) @@ -90,7 +97,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { { mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(suite.reg, nil).Once() - + mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() var scanner scanner.Registration res, err := suite.GetJSON("/projects/1/scanner", &scanner) suite.NoError(err) @@ -101,6 +108,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { { mock.OnAnything(projectCtlMock, "GetByName").Return(suite.project, nil).Once() mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() + mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(suite.reg, nil).Once() var scanner scanner.Registration diff --git a/src/server/v2.0/handler/scan.go b/src/server/v2.0/handler/scan.go index cca0092e8..80b70131f 100644 --- a/src/server/v2.0/handler/scan.go +++ b/src/server/v2.0/handler/scan.go @@ -25,6 +25,7 @@ import ( "github.com/goharbor/harbor/src/controller/scan" "github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/pkg/distribution" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/scan" ) @@ -50,7 +51,15 @@ func (s *scanAPI) Prepare(ctx context.Context, _ string, params interface{}) mid } func (s *scanAPI) StopScanArtifact(ctx context.Context, params operation.StopScanArtifactParams) middleware.Responder { - if err := s.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionStop, rbac.ResourceScan); err != nil { + scanType := v1.ScanTypeVulnerability + if params.ScanType != nil && validScanType(params.ScanType.ScanType) { + scanType = params.ScanType.ScanType + } + res := rbac.ResourceScan + if scanType == v1.ScanTypeSbom { + res = rbac.ResourceSBOM + } + if err := s.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionStop, res); err != nil { return s.SendError(ctx, err) } @@ -68,22 +77,26 @@ func (s *scanAPI) StopScanArtifact(ctx context.Context, params operation.StopSca } func (s *scanAPI) ScanArtifact(ctx context.Context, params operation.ScanArtifactParams) middleware.Responder { - if err := s.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionCreate, rbac.ResourceScan); err != nil { - return s.SendError(ctx, err) - } - - repository := fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName) - artifact, err := s.artCtl.GetByReference(ctx, repository, params.Reference, nil) - if err != nil { - return s.SendError(ctx, err) - } - + scanType := v1.ScanTypeVulnerability options := []scan.Option{} if !distribution.IsDigest(params.Reference) { options = append(options, scan.WithTag(params.Reference)) } if params.ScanRequestType != nil && validScanType(params.ScanRequestType.ScanType) { - options = append(options, scan.WithScanType(params.ScanRequestType.ScanType)) + scanType = params.ScanRequestType.ScanType + options = append(options, scan.WithScanType(scanType)) + } + res := rbac.ResourceScan + if scanType == v1.ScanTypeSbom { + res = rbac.ResourceSBOM + } + if err := s.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionCreate, res); err != nil { + return s.SendError(ctx, err) + } + repository := fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName) + artifact, err := s.artCtl.GetByReference(ctx, repository, params.Reference, nil) + if err != nil { + return s.SendError(ctx, err) } if err := s.scanCtl.Scan(ctx, artifact, options...); err != nil { diff --git a/src/testing/pkg/scan/rest/v1/client.go b/src/testing/pkg/scan/rest/v1/client.go index b17c21e71..1d15012c7 100644 --- a/src/testing/pkg/scan/rest/v1/client.go +++ b/src/testing/pkg/scan/rest/v1/client.go @@ -42,9 +42,9 @@ func (_m *Client) GetMetadata() (*v1.ScannerAdapterMetadata, error) { return r0, r1 } -// GetScanReport provides a mock function with given fields: scanRequestID, reportMIMEType -func (_m *Client) GetScanReport(scanRequestID string, reportMIMEType string) (string, error) { - ret := _m.Called(scanRequestID, reportMIMEType) +// GetScanReport provides a mock function with given fields: scanRequestID, reportMIMEType, urlParameter +func (_m *Client) GetScanReport(scanRequestID string, reportMIMEType string, urlParameter string) (string, error) { + ret := _m.Called(scanRequestID, reportMIMEType, urlParameter) if len(ret) == 0 { panic("no return value specified for GetScanReport") @@ -52,17 +52,17 @@ func (_m *Client) GetScanReport(scanRequestID string, reportMIMEType string) (st var r0 string var r1 error - if rf, ok := ret.Get(0).(func(string, string) (string, error)); ok { - return rf(scanRequestID, reportMIMEType) + if rf, ok := ret.Get(0).(func(string, string, string) (string, error)); ok { + return rf(scanRequestID, reportMIMEType, urlParameter) } - if rf, ok := ret.Get(0).(func(string, string) string); ok { - r0 = rf(scanRequestID, reportMIMEType) + if rf, ok := ret.Get(0).(func(string, string, string) string); ok { + r0 = rf(scanRequestID, reportMIMEType, urlParameter) } else { r0 = ret.Get(0).(string) } - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(scanRequestID, reportMIMEType) + if rf, ok := ret.Get(1).(func(string, string, string) error); ok { + r1 = rf(scanRequestID, reportMIMEType, urlParameter) } else { r1 = ret.Error(1) } From 6709c789fb5a949a25c26c5f1a4e6086bc165ba8 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Wed, 17 Apr 2024 15:52:58 +0800 Subject: [PATCH 025/100] feat: add test case for customizing OIDC provider name (#20287) Signed-off-by: Shengwen Yu --- tests/robot-cases/Group1-Nightly/OIDC.robot | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/robot-cases/Group1-Nightly/OIDC.robot b/tests/robot-cases/Group1-Nightly/OIDC.robot index f4b11079b..a29d1064b 100644 --- a/tests/robot-cases/Group1-Nightly/OIDC.robot +++ b/tests/robot-cases/Group1-Nightly/OIDC.robot @@ -26,6 +26,18 @@ Test Case - Get Harbor Version #Just get harbor version and log it Get Harbor Version +Test Case - Update OIDC Provider Name + [Tags] oidc_provider_name + Init Chrome Driver + Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} is_oidc=${true} + # Set OIDC Provider Name to TestDex + Switch To Configuration Authentication + Retry Text Input //input[@id='oidcName'] TestDex + Retry Element Click ${config_auth_save_button_xpath} + Logout Harbor + Retry Wait Until Page Contains Element //span[normalize-space()='LOGIN WITH TestDex'] + Close Browser + Test Case - OIDC User Sign In #Sign in with all 9 users is for user population, other test cases might use these users. Sign In Harbor With OIDC User ${HARBOR_URL} From fb2e0042d029c0eb9f2f753b2fad287e6c8a04e8 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 17 Apr 2024 17:52:50 +0800 Subject: [PATCH 026/100] Rename scan request type (#20288) Signed-off-by: stonezdj --- api/v2.0/swagger.yaml | 11 ++--------- src/controller/scan/base_controller.go | 3 ++- src/server/v2.0/handler/scan.go | 4 ++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 0f5c18e73..5f0457790 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -1176,11 +1176,11 @@ paths: - $ref: '#/parameters/projectName' - $ref: '#/parameters/repositoryName' - $ref: '#/parameters/reference' - - name: scan_request_type + - name: scanType in: body required: false schema: - $ref: '#/definitions/ScanRequestType' + $ref: '#/definitions/ScanType' responses: '202': $ref: '#/responses/202' @@ -6766,13 +6766,6 @@ definitions: type: string description: Version of the scanner adapter example: "v0.9.1" - ScanRequestType: - type: object - properties: - scan_type: - type: string - description: 'The scan type for the scan request. Two options are currently supported, vulnerability and sbom' - enum: [vulnerability, sbom] ScanOverview: type: object description: 'The scan overview attached in the metadata of tag' diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index be168098a..25f0fc791 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -742,10 +742,11 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, return map[string]interface{}{}, nil } reportContent := reports[0].Report + result := map[string]interface{}{} if len(reportContent) == 0 { log.Warning("no content for current report") + return result, nil } - result := map[string]interface{}{} err = json.Unmarshal([]byte(reportContent), &result) return result, err } diff --git a/src/server/v2.0/handler/scan.go b/src/server/v2.0/handler/scan.go index 80b70131f..a22eaaaed 100644 --- a/src/server/v2.0/handler/scan.go +++ b/src/server/v2.0/handler/scan.go @@ -82,8 +82,8 @@ func (s *scanAPI) ScanArtifact(ctx context.Context, params operation.ScanArtifac if !distribution.IsDigest(params.Reference) { options = append(options, scan.WithTag(params.Reference)) } - if params.ScanRequestType != nil && validScanType(params.ScanRequestType.ScanType) { - scanType = params.ScanRequestType.ScanType + if params.ScanType != nil && validScanType(params.ScanType.ScanType) { + scanType = params.ScanType.ScanType options = append(options, scan.WithScanType(scanType)) } res := rbac.ResourceScan From 2ea7d09412e25a838e217e2c4d52ee4d0f51ec1f Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 17 Apr 2024 22:51:11 +0800 Subject: [PATCH 027/100] skip to log scan sbom accessory for sbom accessory (#20290) Avoid to log the generate SBOM failure message when the artifact is SBOM in webhook event Signed-off-by: stonezdj --- src/controller/event/handler/internal/util.go | 6 +++--- src/controller/event/handler/internal/util_test.go | 6 ++---- src/controller/scan/base_controller.go | 13 ++++++++----- src/controller/scan/options.go | 9 +++++++++ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/controller/event/handler/internal/util.go b/src/controller/event/handler/internal/util.go index cc10e09ca..51085a6f1 100644 --- a/src/controller/event/handler/internal/util.go +++ b/src/controller/event/handler/internal/util.go @@ -22,6 +22,7 @@ import ( "github.com/goharbor/harbor/src/controller/scan" "github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/orm" + v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" ) // autoScan scan artifact when the project of the artifact enable auto scan @@ -38,7 +39,7 @@ func autoScan(ctx context.Context, a *artifact.Artifact, tags ...string) error { return orm.WithTransaction(func(ctx context.Context) error { options := []scan.Option{} if len(tags) > 0 { - options = append(options, scan.WithTag(tags[0])) + options = append(options, scan.WithTag(tags[0]), scan.WithFromEvent(true)) } return scan.DefaultController.Scan(ctx, a, options...) @@ -56,8 +57,7 @@ func autoGenSBOM(ctx context.Context, a *artifact.Artifact) error { // transaction here to work with the image index return orm.WithTransaction(func(ctx context.Context) error { options := []scan.Option{} - // TODO: extract the sbom scan type to a constant - options = append(options, scan.WithScanType("sbom")) + options = append(options, scan.WithScanType(v1.ScanTypeSbom), scan.WithFromEvent(true)) log.Debugf("sbom scan controller artifact %+v, options %+v", a, options) return scan.DefaultController.Scan(ctx, a, options...) })(orm.SetTransactionOpNameToContext(ctx, "tx-auto-gen-sbom")) diff --git a/src/controller/event/handler/internal/util_test.go b/src/controller/event/handler/internal/util_test.go index 4a48378a4..158bcc2fe 100644 --- a/src/controller/event/handler/internal/util_test.go +++ b/src/controller/event/handler/internal/util_test.go @@ -101,9 +101,7 @@ func (suite *AutoScanTestSuite) TestAutoScanSBOM() { proModels.ProMetaAutoSBOMGen: "true", }, }, nil) - - mock.OnAnything(suite.scanController, "Scan").Return(nil) - + suite.scanController.On("Scan", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) art := &artifact.Artifact{} @@ -117,7 +115,7 @@ func (suite *AutoScanTestSuite) TestAutoScanSBOMFalse() { }, }, nil) - mock.OnAnything(suite.scanController, "Scan").Return(nil) + suite.scanController.On("Scan", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) art := &artifact.Artifact{} diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index 25f0fc791..52eb4eefe 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -247,17 +247,20 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti if err != nil { return err } - - if !scannable { - return errors.BadRequestError(nil).WithMessage("the configured scanner %s does not support scanning artifact with mime type %s", r.Name, artifact.ManifestMediaType) - } - // Parse options opts, err := parseOptions(options...) if err != nil { return errors.Wrap(err, "scan controller: scan") } + if !scannable { + if opts.FromEvent { + // skip to return err for event related scan + return nil + } + return errors.BadRequestError(nil).WithMessage("the configured scanner %s does not support scanning artifact with mime type %s", r.Name, artifact.ManifestMediaType) + } + var ( errs []error launchScanJobParams []*launchScanJobParam diff --git a/src/controller/scan/options.go b/src/controller/scan/options.go index 82e4e3d3e..c751ee100 100644 --- a/src/controller/scan/options.go +++ b/src/controller/scan/options.go @@ -21,6 +21,7 @@ type Options struct { ExecutionID int64 // The execution id to scan artifact Tag string // The tag of the artifact to scan ScanType string // The scan type could be sbom or vulnerability + FromEvent bool // indicate the current call from event or not } // GetScanType returns the scan type. for backward compatibility, the default type is vulnerability. @@ -63,3 +64,11 @@ func WithScanType(scanType string) Option { return nil } } + +// WithFromEvent set the caller's source +func WithFromEvent(fromEvent bool) Option { + return func(options *Options) error { + options.FromEvent = fromEvent + return nil + } +} From 4fd11ce072d650217b95c5e2ac84f3b8e5bbec8b Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Thu, 18 Apr 2024 15:26:03 +0900 Subject: [PATCH 028/100] refactor: update controller.go (#20297) minor fix Signed-off-by: Ikko Eltociear Ashimine Co-authored-by: MinerYang --- src/controller/scanner/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controller/scanner/controller.go b/src/controller/scanner/controller.go index 9df9a6d36..d2ae007dd 100644 --- a/src/controller/scanner/controller.go +++ b/src/controller/scanner/controller.go @@ -113,7 +113,7 @@ type Controller interface { // Arguments: // ctx context.Context : the context.Context for this method // projectID int64 : the ID of the given project - // scannerID string : the UUID of the the scanner + // scannerID string : the UUID of the scanner // // Returns: // error : non nil error if any errors occurred From e8907a47ab6301298a2b77042b1408edf24a17c5 Mon Sep 17 00:00:00 2001 From: Lichao Xue <68891670+xuelichao@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:22:11 +0800 Subject: [PATCH 029/100] SBOM UI feature implementation (#19946) * draft: sbom UI feature implementation Signed-off-by: xuelichao * refactor based on swagger yaml changes Signed-off-by: xuelichao * update scan type for scan and stop sbom request Signed-off-by: xuelichao --------- Signed-off-by: xuelichao --- .../create-edit-rule.component.spec.ts | 9 + .../artifact-additions.component.html | 117 ++++++--- .../artifact-additions.component.ts | 45 +++- .../artifact-sbom.component.html | 92 +++++++ .../artifact-sbom.component.scss | 74 ++++++ .../artifact-sbom.component.spec.ts | 194 ++++++++++++++ .../artifact-sbom/artifact-sbom.component.ts | 248 ++++++++++++++++++ .../artifact-list-page.service.ts | 43 +-- .../artifact-list-tab.component.html | 84 ++++-- .../artifact-list-tab.component.spec.ts | 13 +- .../artifact-list-tab.component.ts | 92 +++++-- .../artifact/artifact-summary.component.html | 2 + .../artifact-summary.component.spec.ts | 6 + .../artifact/artifact-summary.component.ts | 10 +- .../repository/artifact/artifact.module.ts | 2 + .../project/repository/artifact/artifact.ts | 195 ++++++++++++++ .../sbom-scanning/sbom-scan.component.spec.ts | 25 +- .../sbom-scanning/sbom-scan.component.ts | 23 +- .../sbom-tip-histogram.component.ts | 6 +- .../result-bar-chart.component.spec.ts | 2 +- .../result-bar-chart.component.ts | 6 +- ...rtifact-detail-routing-resolver.service.ts | 2 +- src/portal/src/app/shared/shared.module.ts | 17 ++ src/portal/src/app/shared/units/utils.ts | 33 +++ src/portal/src/i18n/lang/de-de-lang.json | 5 +- src/portal/src/i18n/lang/en-us-lang.json | 5 +- src/portal/src/i18n/lang/es-es-lang.json | 5 +- src/portal/src/i18n/lang/fr-fr-lang.json | 7 +- src/portal/src/i18n/lang/ko-kr-lang.json | 7 +- src/portal/src/i18n/lang/pt-br-lang.json | 7 +- src/portal/src/i18n/lang/tr-tr-lang.json | 5 +- src/portal/src/i18n/lang/zh-cn-lang.json | 5 +- src/portal/src/i18n/lang/zh-tw-lang.json | 3 +- 33 files changed, 1222 insertions(+), 167 deletions(-) create mode 100644 src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html create mode 100644 src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.scss create mode 100644 src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts create mode 100644 src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts index 3bdf95fa5..19e47f9bc 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts @@ -281,4 +281,13 @@ describe('CreateEditRuleComponent (inline template)', () => { expect(ruleNameInput).toBeTruthy(); expect(ruleNameInput.value.trim()).toEqual('sync_01'); })); + + it('List all Registries Response', fakeAsync(() => { + fixture.detectChanges(); + comp.ngOnInit(); + comp.getAllRegistries(); + fixture.whenStable(); + tick(5000); + expect(comp.targetList.length).toBe(4); + })); }); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html index 20d2950b8..99208d3f4 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html @@ -1,62 +1,105 @@

{{ 'ARTIFACT.ADDITIONS' | translate }}

- + - - - - + + + + + + + + + + + + + - - - - + + + + + - - - - + + + + + - - - - + + + + + - - - - + + + + +
diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts index b31ef5df4..45994ac8e 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts @@ -1,15 +1,23 @@ -import { Component, Input } from '@angular/core'; +import { + AfterViewChecked, + ChangeDetectorRef, + Component, + Input, + OnInit, + ViewChild, +} from '@angular/core'; import { ADDITIONS } from './models'; import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/addition-links'; import { AdditionLink } from '../../../../../../../ng-swagger-gen/models/addition-link'; import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact'; +import { ClrTabs } from '@clr/angular'; @Component({ selector: 'artifact-additions', templateUrl: './artifact-additions.component.html', styleUrls: ['./artifact-additions.component.scss'], }) -export class ArtifactAdditionsComponent { +export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit { @Input() artifact: Artifact; @Input() additionLinks: AdditionLinks; @Input() projectName: string; @@ -19,7 +27,28 @@ export class ArtifactAdditionsComponent { repoName: string; @Input() digest: string; - constructor() {} + @Input() + sbomDigest: string; + @Input() + tab: string; + + @Input() currentTabLinkId: string = 'vulnerability'; + activeTab: string = null; + + @ViewChild('additionsTab') tabs: ClrTabs; + constructor(private ref: ChangeDetectorRef) {} + + ngOnInit(): void { + this.activeTab = this.tab; + } + + ngAfterViewChecked() { + if (this.activeTab) { + this.currentTabLinkId = this.activeTab; + this.activeTab = null; + } + this.ref.detectChanges(); + } getVulnerability(): AdditionLink { if ( @@ -30,6 +59,12 @@ export class ArtifactAdditionsComponent { } return null; } + getSbom(): AdditionLink { + if (this.additionLinks && this.additionLinks[ADDITIONS.SBOMS]) { + return this.additionLinks[ADDITIONS.SBOMS]; + } + return {}; + } getBuildHistory(): AdditionLink { if (this.additionLinks && this.additionLinks[ADDITIONS.BUILD_HISTORY]) { return this.additionLinks[ADDITIONS.BUILD_HISTORY]; @@ -54,4 +89,8 @@ export class ArtifactAdditionsComponent { } return null; } + + actionTab(tab: string): void { + this.currentTabLinkId = tab; + } } diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html new file mode 100644 index 000000000..c7b9cf8a6 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html @@ -0,0 +1,92 @@ +
+
+
+
+ +
+
+
+
+ + +
+
+ +
+
+
+ {{ + 'SBOM.GRID.COLUMN_PACKAGE' | translate + }} + {{ + 'SBOM.GRID.COLUMN_VERSION' | translate + }} + {{ + 'SBOM.GRID.COLUMN_LICENSE' | translate + }} + + {{ + 'SBOM.STATE.OTHER_STATUS' | translate + }} + + {{ 'SBOM.CHART.TOOLTIPS_TITLE_ZERO' | translate }} + + + + {{ + res.name ?? '' + }} + {{ + res.versionInfo ?? '' + }} + {{ res.licenseConcluded ?? '' }} + + + +
+ {{ + 'SBOM.REPORTED_BY' + | translate + : { + scanner: getScannerInfo( + artifact?.sbom_overview?.scanner + ) + } + }} +
+ + {{ + 'PAGINATION.PAGE_SIZE' | translate + }} + {{ pagination.firstItem + 1 }} - + {{ pagination.lastItem + 1 }} + {{ 'SBOM.GRID.FOOT_OF' | translate }} + {{ artifactSbomPackages().length }} + {{ 'SBOM.GRID.FOOT_ITEMS' | translate }} + +
+
+
+
diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.scss b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.scss new file mode 100644 index 000000000..9ec2fa919 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.scss @@ -0,0 +1,74 @@ +.result-row { + position: relative; +} +/* stylelint-disable */ +.rightPos{ + position: absolute; + z-index: 100; + right: 0; + margin-top: 1.25rem; +} + +.option-right { + padding-right: 16px; + margin-top: 5px; +} + +.center { + align-items: center; +} + +.label-critical { + background:#ff4d2e; + color:#000; +} + +.label-danger { + background:#ff8f3d!important; + color:#000!important; +} + +.label-medium { + background-color: #ffce66; + color:#000; +} + +.label-low { + background: #fff1ad; + color:#000; +} + +.label-none { + background-color: #2ec0ff; + color:#000; +} + +.no-border { + border: none; +} + +.ml-05 { + margin-left: 0.5rem; +} + +.report { + text-align: left; +} + +.mt-5px { + margin-top: 5px; +} + +.label { + min-width: 3rem; +} + +.package-medium { + max-width: 25rem; + text-overflow: ellipsis; +} + +.version-medium { + min-width: 22rem; + text-overflow: ellipsis; +} diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts new file mode 100644 index 000000000..e3978ad39 --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts @@ -0,0 +1,194 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ArtifactSbomComponent } from './artifact-sbom.component'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ClarityModule } from '@clr/angular'; +import { of } from 'rxjs'; +import { + TranslateFakeLoader, + TranslateLoader, + TranslateModule, +} from '@ngx-translate/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { UserPermissionService } from '../../../../../../shared/services'; +import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link'; +import { ErrorHandler } from '../../../../../../shared/units/error-handler'; +import { SessionService } from '../../../../../../shared/services/session.service'; +import { SessionUser } from '../../../../../../shared/entities/session-user'; +import { AppConfigService } from 'src/app/services/app-config.service'; +import { ArtifactSbomPackageItem } from '../../artifact'; +import { ArtifactService } from 'ng-swagger-gen/services'; +import { ArtifactListPageService } from '../../artifact-list-page/artifact-list-page.service'; + +describe('ArtifactSbomComponent', () => { + let component: ArtifactSbomComponent; + let fixture: ComponentFixture; + const artifactSbomPackages: ArtifactSbomPackageItem[] = [ + { + name: 'alpine-baselayout', + SPDXID: 'SPDXRef-Package-5b53573c19a59415', + versionInfo: '3.2.0-r18', + supplier: 'NOASSERTION', + downloadLocation: 'NONE', + checksums: [ + { + algorithm: 'SHA1', + checksumValue: '132992eab020986b3b5d886a77212889680467a0', + }, + ], + sourceInfo: 'built package from: alpine-baselayout 3.2.0-r18', + licenseConcluded: 'GPL-2.0-only', + licenseDeclared: 'GPL-2.0-only', + copyrightText: '', + externalRefs: [ + { + referenceCategory: 'PACKAGE-MANAGER', + referenceType: 'purl', + referenceLocator: + 'pkg:apk/alpine/alpine-baselayout@3.2.0-r18?arch=x86_64\u0026distro=3.15.5', + }, + ], + attributionTexts: [ + 'PkgID: alpine-baselayout@3.2.0-r18', + 'LayerDiffID: sha256:ad543cd673bd9de2bac48599da992506dcc37a183179302ea934853aaa92cb84', + ], + primaryPackagePurpose: 'LIBRARY', + }, + { + name: 'alpine-keys', + SPDXID: 'SPDXRef-Package-7e5952f7a76e9643', + versionInfo: '2.4-r1', + supplier: 'NOASSERTION', + downloadLocation: 'NONE', + checksums: [ + { + algorithm: 'SHA1', + checksumValue: '903176b2d2a8ddefd1ba6940f19ad17c2c1d4aff', + }, + ], + sourceInfo: 'built package from: alpine-keys 2.4-r1', + licenseConcluded: 'MIT', + licenseDeclared: 'MIT', + copyrightText: '', + externalRefs: [ + { + referenceCategory: 'PACKAGE-MANAGER', + referenceType: 'purl', + referenceLocator: + 'pkg:apk/alpine/alpine-keys@2.4-r1?arch=x86_64\u0026distro=3.15.5', + }, + ], + attributionTexts: [ + 'PkgID: alpine-keys@2.4-r1', + 'LayerDiffID: sha256:ad543cd673bd9de2bac48599da992506dcc37a183179302ea934853aaa92cb84', + ], + primaryPackagePurpose: 'LIBRARY', + }, + ]; + const artifactSbomJson = { + spdxVersion: 'SPDX-2.3', + dataLicense: 'CC0-1.0', + SPDXID: 'SPDXRef-DOCUMENT', + name: 'alpine:3.15.5', + documentNamespace: + 'http://aquasecurity.github.io/trivy/container_image/alpine:3.15.5-7ead854c-7340-44c9-bbbf-5403c21cc9b6', + creationInfo: { + licenseListVersion: '', + creators: ['Organization: aquasecurity', 'Tool: trivy-0.47.0'], + created: '2023-11-29T07:06:22Z', + }, + packages: artifactSbomPackages, + }; + const fakedArtifactService = { + getAddition() { + return of(JSON.stringify(artifactSbomJson)); + }, + }; + const fakedUserPermissionService = { + hasProjectPermissions() { + return of(true); + }, + }; + const fakedAppConfigService = { + getConfig() { + return of({ sbom_enabled: true }); + }, + }; + const mockedUser: SessionUser = { + user_id: 1, + username: 'admin', + email: 'harbor@vmware.com', + realname: 'admin', + has_admin_role: true, + comment: 'no comment', + }; + const fakedSessionService = { + getCurrentUser() { + return mockedUser; + }, + }; + const mockedSbomDigest = + 'sha256:51a41cec9de9d62ee60e206f5a8a615a028a65653e45539990867417cb486285'; + const mockedArtifactListPageService = { + hasScannerSupportSBOM(): boolean { + return true; + }, + init() {}, + }; + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + ClarityModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader, + }, + }), + ], + declarations: [ArtifactSbomComponent], + providers: [ + ErrorHandler, + { provide: AppConfigService, useValue: fakedAppConfigService }, + { provide: ArtifactService, useValue: fakedArtifactService }, + { + provide: UserPermissionService, + useValue: fakedUserPermissionService, + }, + { provide: SessionService, useValue: fakedSessionService }, + { + provide: ArtifactListPageService, + useValue: mockedArtifactListPageService, + }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ArtifactSbomComponent); + component = fixture.componentInstance; + component.hasSbomPermission = true; + component.hasScannerSupportSBOM = true; + component.sbomDigest = mockedSbomDigest; + component.ngOnInit(); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + it('should get sbom list and render', async () => { + fixture.detectChanges(); + await fixture.whenStable(); + const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row'); + expect(rows.length).toEqual(2); + }); + + it('download button should show the right text', async () => { + fixture.autoDetectChanges(true); + const scanBtn: HTMLButtonElement = + fixture.nativeElement.querySelector('#sbom-btn'); + expect(scanBtn.innerText).toContain('SBOM.DOWNLOAD'); + }); +}); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts new file mode 100644 index 000000000..ac352ff0f --- /dev/null +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts @@ -0,0 +1,248 @@ +import { + AfterViewInit, + Component, + Input, + OnDestroy, + OnInit, +} from '@angular/core'; +import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular'; +import { finalize } from 'rxjs/operators'; +import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link'; +import { + ScannerVo, + UserPermissionService, + USERSTATICPERMISSION, +} from '../../../../../../shared/services'; +import { ErrorHandler } from '../../../../../../shared/units/error-handler'; +import { + dbEncodeURIComponent, + downloadJson, + getPageSizeFromLocalStorage, + PageSizeMapKeys, + SBOM_SCAN_STATUS, + setPageSizeToLocalStorage, +} from '../../../../../../shared/units/utils'; +import { Subscription } from 'rxjs'; +import { Artifact } from '../../../../../../../../ng-swagger-gen/models/artifact'; +import { SessionService } from '../../../../../../shared/services/session.service'; +import { + EventService, + HarborEvent, +} from '../../../../../../services/event-service/event.service'; +import { severityText } from '../../../../../left-side-nav/interrogation-services/vulnerability-database/security-hub.interface'; +import { AppConfigService } from 'src/app/services/app-config.service'; + +import { + ArtifactSbom, + ArtifactSbomPackageItem, + getArtifactSbom, +} from '../../artifact'; +import { ArtifactService } from 'ng-swagger-gen/services'; +import { ScanTypes } from 'src/app/shared/entities/shared.const'; +import { ArtifactListPageService } from '../../artifact-list-page/artifact-list-page.service'; + +@Component({ + selector: 'hbr-artifact-sbom', + templateUrl: './artifact-sbom.component.html', + styleUrls: ['./artifact-sbom.component.scss'], +}) +export class ArtifactSbomComponent implements OnInit, OnDestroy { + @Input() + projectName: string; + @Input() + projectId: number; + @Input() + repoName: string; + @Input() + sbomDigest: string; + @Input() artifact: Artifact; + + artifactSbom: ArtifactSbom; + loading: boolean = false; + hasScannerSupportSBOM: boolean = false; + downloadSbomBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; + hasSbomPermission: boolean = false; + + hasShowLoading: boolean = false; + sub: Subscription; + hasViewInitWithDelay: boolean = false; + pageSize: number = getPageSizeFromLocalStorage( + PageSizeMapKeys.ARTIFACT_SBOM_COMPONENT, + 25 + ); + readonly severityText = severityText; + constructor( + private errorHandler: ErrorHandler, + private appConfigService: AppConfigService, + private artifactService: ArtifactService, + private artifactListPageService: ArtifactListPageService, + private userPermissionService: UserPermissionService, + private eventService: EventService, + private session: SessionService + ) {} + + ngOnInit() { + this.artifactListPageService.init(this.projectId); + this.getSbom(); + this.getSbomPermission(); + if (!this.sub) { + this.sub = this.eventService.subscribe( + HarborEvent.UPDATE_SBOM_INFO, + (artifact: Artifact) => { + if (artifact?.digest === this.artifact?.digest) { + if (artifact.sbom_overview) { + const sbomDigest = Object.values( + artifact.sbom_overview + )?.[0]?.sbom_digest; + if (sbomDigest) { + this.sbomDigest = sbomDigest; + } + } + this.getSbom(); + } + } + ); + } + setTimeout(() => { + this.hasViewInitWithDelay = true; + }, 0); + } + + ngOnDestroy() { + if (this.sub) { + this.sub.unsubscribe(); + this.sub = null; + } + } + + getSbom() { + if (this.sbomDigest) { + if (!this.hasShowLoading) { + this.loading = true; + this.hasShowLoading = true; + } + const sbomAdditionParams = { + repositoryName: dbEncodeURIComponent(this.repoName), + reference: this.sbomDigest, + projectName: this.projectName, + addition: ScanTypes.SBOM, + }; + this.artifactService + .getAddition(sbomAdditionParams) + .pipe( + finalize(() => { + this.loading = false; + this.hasShowLoading = false; + }) + ) + .subscribe( + res => { + if (res) { + this.artifactSbom = getArtifactSbom( + JSON.parse(res) + ); + } else { + this.loading = false; + this.hasShowLoading = false; + } + }, + error => { + this.errorHandler.error(error); + } + ); + } + } + + getSbomPermission(): void { + const permissions = [ + { + resource: USERSTATICPERMISSION.REPOSITORY_TAG_SBOM_JOB.KEY, + action: USERSTATICPERMISSION.REPOSITORY_TAG_SBOM_JOB.VALUE.READ, + }, + ]; + this.userPermissionService + .hasProjectPermissions(this.projectId, permissions) + .subscribe( + (results: Array) => { + this.hasSbomPermission = results[0]; + // only has label permission + }, + error => this.errorHandler.error(error) + ); + } + + refresh(): void { + this.getSbom(); + } + + hasGeneratedSbom(): boolean { + return this.hasViewInitWithDelay; + } + + isSystemAdmin(): boolean { + const account = this.session.getCurrentUser(); + return account && account.has_admin_role; + } + + getScannerInfo(scanner: ScannerVo): string { + if (scanner) { + if (scanner.name && scanner.version) { + return `${scanner.name}@${scanner.version}`; + } + if (scanner.name && !scanner.version) { + return `${scanner.name}`; + } + } + return ''; + } + + isRunningState(): boolean { + return ( + this.hasViewInitWithDelay && + this.artifact.sbom_overview && + (this.artifact.sbom_overview.scan_status === + SBOM_SCAN_STATUS.PENDING || + this.artifact.sbom_overview.scan_status === + SBOM_SCAN_STATUS.RUNNING) + ); + } + + downloadSbom() { + this.downloadSbomBtnState = ClrLoadingState.LOADING; + if ( + this.artifact?.sbom_overview?.scan_status === + SBOM_SCAN_STATUS.SUCCESS + ) { + downloadJson( + this.artifactSbom.sbomJsonRaw, + `${this.artifactSbom.sbomName}.json` + ); + } + this.downloadSbomBtnState = ClrLoadingState.DEFAULT; + } + + canDownloadSbom(): boolean { + this.hasScannerSupportSBOM = + this.artifactListPageService.hasScannerSupportSBOM(); + return ( + this.hasScannerSupportSBOM && + //this.hasSbomPermission && + this.sbomDigest && + this.downloadSbomBtnState !== ClrLoadingState.LOADING && + this.artifactSbom !== undefined + ); + } + + artifactSbomPackages(): ArtifactSbomPackageItem[] { + return this.artifactSbom?.sbomPackage?.packages ?? []; + } + + load(state: ClrDatagridStateInterface) { + if (state?.page?.size) { + setPageSizeToLocalStorage( + PageSizeMapKeys.ARTIFACT_SBOM_COMPONENT, + state.page.size + ); + } + } +} diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts index a858938dd..daf0c0ae5 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list-page.service.ts @@ -6,6 +6,7 @@ import { USERSTATICPERMISSION, } from '../../../../../shared/services'; import { ErrorHandler } from '../../../../../shared/units/error-handler'; +import { Scanner } from '../../../../left-side-nav/interrogation-services/scanner/scanner'; @Injectable() export class ArtifactListPageService { @@ -19,6 +20,7 @@ export class ArtifactListPageService { private _hasDeleteImagePermission: boolean = false; private _hasScanImagePermission: boolean = false; private _hasSbomPermission: boolean = false; + private _scanner: Scanner = undefined; constructor( private scanningService: ScanningResultService, @@ -26,6 +28,10 @@ export class ArtifactListPageService { private errorHandlerService: ErrorHandler ) {} + getProjectScanner(): Scanner { + return this._scanner; + } + getScanBtnState(): ClrLoadingState { return this._scanBtnState; } @@ -103,29 +109,28 @@ export class ArtifactListPageService { this._sbomBtnState = ClrLoadingState.LOADING; this.scanningService.getProjectScanner(projectId).subscribe( response => { - if ( - response && - '{}' !== JSON.stringify(response) && - !response.disabled && - response.health === 'healthy' - ) { - this.updateStates( - true, - ClrLoadingState.SUCCESS, - ClrLoadingState.SUCCESS - ); - if (response?.capabilities) { - this.updateCapabilities(response?.capabilities); + if (response && '{}' !== JSON.stringify(response)) { + this._scanner = response; + if (!response.disabled && response.health === 'healthy') { + this.updateStates( + true, + ClrLoadingState.SUCCESS, + ClrLoadingState.SUCCESS + ); + if (response?.capabilities) { + this.updateCapabilities(response?.capabilities); + } + } else { + this.updateStates( + false, + ClrLoadingState.ERROR, + ClrLoadingState.ERROR + ); } - } else { - this.updateStates( - false, - ClrLoadingState.ERROR, - ClrLoadingState.ERROR - ); } }, error => { + this._scanner = null; this.updateStates( false, ClrLoadingState.ERROR, diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html index 1001da144..d523b67c1 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html @@ -24,6 +24,7 @@ canScanNow() && selectedRowHasVul() && hasEnabledScanner && + hasScannerSupportVulnerability && hasScanImagePermission ) " @@ -32,44 +33,26 @@ >  {{ 'VULNERABILITY.SCAN_NOW' | translate }} - - + + + + +
{{ 'REPOSITORY.COPY_DIGEST_ID' | translate }}
+ +
@@ -432,7 +462,7 @@ (submitStopFinish)="submitSbomStopFinish($event)" (scanFinished)="sbomFinished($event)" *ngIf="hasScannerSupportSBOM" - [inputScanner]="artifact?.sbom_overview?.scanner" + [inputScanner]="projectScanner" (submitFinish)="submitSbomFinish($event)" [projectName]="projectName" [projectId]="projectId" diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts index 27010f065..bb45710a1 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.spec.ts @@ -27,11 +27,17 @@ import { ArtifactModule } from '../../../artifact.module'; import { SBOM_SCAN_STATUS, VULNERABILITY_SCAN_STATUS, -} from 'src/app/shared/units/utils'; +} from '../../../../../../../shared/units/utils'; +import { Scanner } from '../../../../../../left-side-nav/interrogation-services/scanner/scanner'; describe('ArtifactListTabComponent', () => { let comp: ArtifactListTabComponent; let fixture: ComponentFixture; + const mockScanner = { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }; const mockActivatedRoute = { snapshot: { params: { @@ -274,6 +280,9 @@ describe('ArtifactListTabComponent', () => { hasScanImagePermission(): boolean { return true; }, + getProjectScanner(): Scanner { + return mockScanner; + }, init() {}, }; beforeEach(async () => { @@ -384,7 +393,7 @@ describe('ArtifactListTabComponent', () => { fixture.nativeElement.querySelector('#generate-sbom-btn'); fixture.detectChanges(); await fixture.whenStable(); - expect(generatedButton.disabled).toBeTruthy(); + expect(generatedButton.disabled).toBeFalsy(); }); it('Stop SBOM button should be disabled', async () => { await fixture.whenStable(); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts index 4675031dc..f73b448bb 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts @@ -84,6 +84,7 @@ import { Accessory } from 'ng-swagger-gen/models/accessory'; import { Tag } from '../../../../../../../../../ng-swagger-gen/models/tag'; import { CopyArtifactComponent } from './copy-artifact/copy-artifact.component'; import { CopyDigestComponent } from './copy-digest/copy-digest.component'; +import { Scanner } from '../../../../../../left-side-nav/interrogation-services/scanner/scanner'; export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z'; @@ -160,6 +161,10 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { get generateSbomBtnState(): ClrLoadingState { return this.artifactListPageService.getSbomBtnState(); } + get projectScanner(): Scanner { + return this.artifactListPageService.getProjectScanner(); + } + onSendingScanCommand: boolean; onSendingStopScanCommand: boolean = false; onStopScanArtifactsLength: number = 0; @@ -190,7 +195,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { false, false, false, - true, + false, true, false, false, @@ -269,6 +274,9 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } ); } + if (this.projectId) { + this.artifactListPageService.init(this.projectId); + } } ngOnDestroy() { @@ -360,7 +368,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { withImmutableStatus: true, withLabel: true, withScanOverview: true, - // withSbomOverview: true, + withSbomOverview: true, withTag: false, XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, withAccessory: false, @@ -385,7 +393,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { withImmutableStatus: true, withLabel: true, withScanOverview: true, - // withSbomOverview: true, + withSbomOverview: true, withTag: false, XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, @@ -435,6 +443,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { repositoryName: dbEncodeURIComponent(this.repoName), withLabel: true, withScanOverview: true, + withSbomOverview: true, withTag: false, sort: getSortingString(state), XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, @@ -749,11 +758,15 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { if (this.activatedRoute.snapshot.queryParams[UN_LOGGED_PARAM] === YES) { this.router.navigate(relativeRouterLink, { relativeTo: this.activatedRoute, - queryParams: { [UN_LOGGED_PARAM]: YES }, + queryParams: { + [UN_LOGGED_PARAM]: YES, + sbomDigest: artifact.sbomDigest ?? '', + }, }); } else { this.router.navigate(relativeRouterLink, { relativeTo: this.activatedRoute, + queryParams: { sbomDigest: artifact.sbomDigest ?? '' }, }); } } @@ -800,6 +813,26 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } return false; } + // if has running job, return false + canGenerateSbomNow(): boolean { + if (!this.hasSbomPermission) { + return false; + } + if (this.onSendingSbomCommand) { + return false; + } + if (this.selectedRow && this.selectedRow.length) { + let flag: boolean = true; + this.selectedRow.forEach(item => { + const st: string = this.sbomStatus(item); + if (this.isRunningState(st)) { + flag = false; + } + }); + return flag; + } + return false; + } // Trigger scan scanNow(): void { if (!this.selectedRow.length) { @@ -816,6 +849,22 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { ); }); } + // Generate SBOM + generateSbom(): void { + if (!this.selectedRow.length) { + return; + } + this.sbomFinishedArtifactLength = 0; + this.onSbomArtifactsLength = this.selectedRow.length; + this.onSendingSbomCommand = true; + this.selectedRow.forEach((data: any) => { + let digest = data.digest; + this.eventService.publish( + HarborEvent.START_GENERATE_SBOM, + this.repoName + '/' + digest + ); + }); + } selectedRowHasVul(): boolean { return !!( @@ -941,7 +990,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } } } - // when finished, remove it from selectedRow sbomFinished(artifact: Artifact) { this.scanFinished(artifact); @@ -1019,18 +1067,17 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { }); } } - checkCosignAndSbomAsync(artifacts: ArtifactFront[]) { if (artifacts) { if (artifacts.length) { artifacts.forEach(item => { item.signed = CHECKING; - // const sbomOverview = item?.sbom_overview; - // item.sbomDigest = sbomOverview?.sbom_digest; - // let queryTypes = `${AccessoryType.COSIGN} ${AccessoryType.NOTATION}`; - // if (!item.sbomDigest) { - // queryTypes = `${queryTypes} ${AccessoryType.SBOM}`; - // } + const sbomOverview = item?.sbom_overview; + item.sbomDigest = sbomOverview?.sbom_digest; + let queryTypes = `${AccessoryType.COSIGN} ${AccessoryType.NOTATION}`; + if (!item.sbomDigest) { + queryTypes = `${queryTypes} ${AccessoryType.SBOM}`; + } this.newArtifactService .listAccessories({ projectName: this.projectName, @@ -1038,16 +1085,21 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { reference: item.digest, page: 1, pageSize: ACCESSORY_PAGE_SIZE, - q: encodeURIComponent( - `type={${AccessoryType.COSIGN} ${AccessoryType.NOTATION}}` - ), + q: encodeURIComponent(`type={${queryTypes}}`), }) .subscribe({ next: res => { - if (res?.length) { - item.signed = TRUE; - } else { - item.signed = FALSE; + item.signed = res?.filter( + item => item.type !== AccessoryType.SBOM + )?.length + ? TRUE + : FALSE; + if (!item.sbomDigest) { + item.sbomDigest = + res?.filter( + item => + item.type === AccessoryType.SBOM + )?.[0]?.digest ?? null; } }, error: err => { @@ -1075,7 +1127,6 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } return false; } - // return true if all selected rows are in "running" state canStopSbom(): boolean { if (this.onSendingStopSbomCommand) { @@ -1142,6 +1193,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { } return null; } + deleteAccessory(a: Accessory) { let titleKey: string, summaryKey: string, diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.html index 436363325..05465eae3 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.html @@ -59,6 +59,8 @@ [projectId]="projectId" [repoName]="repositoryName" [digest]="artifactDigest" + [sbomDigest]="sbomDigest" + [tab]="activeTab" [additionLinks]="artifact?.addition_links">
diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.spec.ts index 9a6a0bf2f..1a7944e83 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.spec.ts @@ -30,6 +30,8 @@ describe('ArtifactSummaryComponent', () => { return undefined; }, }; + const mockedSbomDigest = + 'sha256:51a41cec9de9d62ee60e206f5a8a615a028a65653e45539990867417cb486285'; let component: ArtifactSummaryComponent; let fixture: ComponentFixture; const mockActivatedRoute = { @@ -42,6 +44,9 @@ describe('ArtifactSummaryComponent', () => { return of(null); }, }, + queryParams: { + sbomDigest: mockedSbomDigest, + }, parent: { params: { id: 1, @@ -89,6 +94,7 @@ describe('ArtifactSummaryComponent', () => { component = fixture.componentInstance; component.repositoryName = 'demo'; component.artifactDigest = 'sha: acf4234f'; + component.sbomDigest = mockedSbomDigest; fixture.detectChanges(); }); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.ts index 7aa95189d..de2f96444 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-summary.component.ts @@ -1,10 +1,7 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Artifact } from '../../../../../../ng-swagger-gen/models/artifact'; -import { ErrorHandler } from '../../../../shared/units/error-handler'; import { Label } from '../../../../../../ng-swagger-gen/models/label'; -import { ProjectService } from '../../../../shared/services'; import { ActivatedRoute, Router } from '@angular/router'; -import { AppConfigService } from '../../../../services/app-config.service'; import { Project } from '../../project'; import { artifactDefault } from './artifact'; import { SafeUrl } from '@angular/platform-browser'; @@ -24,6 +21,8 @@ import { export class ArtifactSummaryComponent implements OnInit { tagId: string; artifactDigest: string; + sbomDigest?: string; + activeTab?: string; repositoryName: string; projectId: string | number; referArtifactNameArray: string[] = []; @@ -37,10 +36,7 @@ export class ArtifactSummaryComponent implements OnInit { loading: boolean = false; constructor( - private projectService: ProjectService, - private errorHandler: ErrorHandler, private route: ActivatedRoute, - private appConfigService: AppConfigService, private router: Router, private frontEndArtifactService: ArtifactService, private event: EventService @@ -100,6 +96,8 @@ export class ArtifactSummaryComponent implements OnInit { this.repositoryName = this.route.snapshot.params['repo']; this.artifactDigest = this.route.snapshot.params['digest']; this.projectId = this.route.snapshot.parent.params['id']; + this.sbomDigest = this.route.snapshot.queryParams['sbomDigest']; + this.activeTab = this.route.snapshot.queryParams['tab']; if (this.repositoryName && this.artifactDigest) { const resolverData = this.route.snapshot.data; if (resolverData) { diff --git a/src/portal/src/app/base/project/repository/artifact/artifact.module.ts b/src/portal/src/app/base/project/repository/artifact/artifact.module.ts index 4ff6da2d4..0272818b8 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact.module.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact.module.ts @@ -12,6 +12,7 @@ import { SummaryComponent } from './artifact-additions/summary/summary.component import { DependenciesComponent } from './artifact-additions/dependencies/dependencies.component'; import { BuildHistoryComponent } from './artifact-additions/build-history/build-history.component'; import { ArtifactVulnerabilitiesComponent } from './artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component'; +import { ArtifactSbomComponent } from './artifact-additions/artifact-sbom/artifact-sbom.component'; import { ArtifactDefaultService, ArtifactService } from './artifact.service'; import { ArtifactDetailRoutingResolverService } from '../../../../services/routing-resolvers/artifact-detail-routing-resolver.service'; import { ResultBarChartComponent } from './vulnerability-scanning/result-bar-chart.component'; @@ -80,6 +81,7 @@ const routes: Routes = [ SummaryComponent, DependenciesComponent, BuildHistoryComponent, + ArtifactSbomComponent, ArtifactVulnerabilitiesComponent, ResultBarChartComponent, ResultSbomComponent, diff --git a/src/portal/src/app/base/project/repository/artifact/artifact.ts b/src/portal/src/app/base/project/repository/artifact/artifact.ts index 9a2c379ae..fa3e19c7d 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact.ts @@ -10,6 +10,7 @@ export interface ArtifactFront extends Artifact { annotationsArray?: Array<{ [key: string]: any }>; tagNumber?: number; signed?: string; + sbomDigest?: string; accessoryNumber?: number; } @@ -75,6 +76,7 @@ export enum AccessoryType { COSIGN = 'signature.cosign', NOTATION = 'signature.notation', NYDUS = 'accelerator.nydus', + SBOM = 'harbor.sbom', } export enum ArtifactType { @@ -166,3 +168,196 @@ export enum ClientNames { CHART = 'Helm', CNAB = 'CNAB', } + +export enum ArtifactSbomType { + SPDX = 'SPDX', +} + +export interface ArtifactSbomPackageItem { + name?: string; + versionInfo?: string; + licenseConcluded?: string; + [key: string]: Object; +} + +export interface ArtifactSbomPackage { + packages: ArtifactSbomPackageItem[]; +} + +export interface ArtifactSbom { + sbomType: ArtifactSbomType; + sbomVersion: string; + sbomName?: string; + sbomDataLicense?: string; + sbomId?: string; + sbomDocumentNamespace?: string; + sbomCreated?: string; + sbomPackage?: ArtifactSbomPackage; + sbomJsonRaw?: Object; +} + +export const ArtifactSbomFieldMapper = { + sbomVersion: 'spdxVersion', + sbomName: 'name', + sbomDataLicense: 'dataLicense', + sbomId: 'SPDXID', + sbomDocumentNamespace: 'documentNamespace', + sbomCreated: 'creationInfo.created', + sbomPackage: { + packages: ['name', 'versionInfo', 'licenseConcluded'], + }, +}; + +/** + * Identify the sbomJson contains the two main properties 'spdxVersion' and 'SPDXID'. + * @param sbomJson SBOM JSON report object. + * @returns true or false + * Return true when the sbomJson object contains the attribues 'spdxVersion' and 'SPDXID'. + * else return false. + */ +export function isSpdxSbom(sbomJson?: Object): boolean { + return Object.keys(sbomJson ?? {}).includes(ArtifactSbomFieldMapper.sbomId); +} + +/** + * Update the value to the data object with the field path. + * @param fieldPath field class path eg {a: {b:'test'}}. field path for b is 'a.b' + * @param data The target object to receive the value. + * @param value The value will be set to the data object. + */ +export function updateObjectWithFieldPath( + fieldPath: string, + data: Object, + value: Object +) { + if (fieldPath && data) { + const fields = fieldPath?.split('.'); + let tempData = data; + fields.forEach((field, index) => { + const properties = Object.getOwnPropertyNames(tempData); + if (field !== '__proto__' && field !== 'constructor') { + if (index === fields.length - 1) { + tempData[field] = value; + } else { + if (!properties.includes(field)) { + tempData[field] = {}; + } + tempData = tempData[field]; + } + } + }); + } +} + +/** + * Get value from data object with field path. + * @param fieldPath field class path eg {a: {b:'test'}}. field path for b is 'a.b' + * @param data The data source target object. + * @returns The value read from data object. + */ +export const getValueFromObjectWithFieldPath = ( + fieldPath: string, + data: Object +) => { + let tempObject = data; + if (fieldPath && data) { + const fields = fieldPath?.split('.'); + fields.forEach(field => { + if (tempObject) { + tempObject = tempObject[field] ?? null; + } + }); + } + return tempObject; +}; + +/** + * Get value from source data object with field path. + * @param fieldPathObject The Object that contains the field paths. + * If we have an Object - {a: {b: 'test', c: [{ d: 2, e: 'v'}]}}. + * The field path for b is 'a.b'. + * The field path for c is {'a.c': ['d', 'e']'}. + * @param sourceData The data source target object. + * @returns the value by field class path. + */ +export function readDataFromArtifactSbomJson( + fieldPathObject: Object, + sourceData: Object +): Object { + let result = null; + if (sourceData) { + switch (typeof fieldPathObject) { + case 'string': + result = getValueFromObjectWithFieldPath( + fieldPathObject, + sourceData + ); + break; + case 'object': + if ( + Array.isArray(fieldPathObject) && + Array.isArray(sourceData) + ) { + result = sourceData.map(source => { + let arrayItem = {}; + fieldPathObject.forEach(field => { + updateObjectWithFieldPath( + field, + arrayItem, + readDataFromArtifactSbomJson(field, source) + ); + }); + return arrayItem; + }); + } else { + const fields = Object.getOwnPropertyNames(fieldPathObject); + result = result ? result : {}; + fields.forEach(field => { + if (sourceData[field]) { + updateObjectWithFieldPath( + field, + result, + readDataFromArtifactSbomJson( + fieldPathObject[field], + sourceData[field] + ) + ); + } + }); + } + break; + default: + break; + } + } + return result; +} + +/** + * Convert SBOM Json report to ArtifactSbom + * @param sbomJson SBOM report in Json format + * @returns ArtifactSbom || null + */ +export function getArtifactSbom(sbomJson?: Object): ArtifactSbom { + if (sbomJson) { + if (isSpdxSbom(sbomJson)) { + const artifactSbom = {}; + artifactSbom.sbomJsonRaw = sbomJson; + artifactSbom.sbomType = ArtifactSbomType.SPDX; + // only retrieve the fields defined in ArtifactSbomFieldMapper + const fields = Object.getOwnPropertyNames(ArtifactSbomFieldMapper); + fields.forEach(field => { + updateObjectWithFieldPath( + field, + artifactSbom, + readDataFromArtifactSbomJson( + ArtifactSbomFieldMapper[field], + sbomJson + ) + ); + }); + return artifactSbom; + } + } + return null; +} diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts index af74ddde8..0f8398c59 100644 --- a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.spec.ts @@ -10,7 +10,6 @@ import { SbomTipHistogramComponent } from './sbom-tip-histogram/sbom-tip-histogr import { SBOMOverview } from './sbom-overview'; import { of, timer } from 'rxjs'; import { ArtifactService, ScanService } from 'ng-swagger-gen/services'; -import { Artifact } from 'ng-swagger-gen/models'; describe('ResultSbomComponent (inline template)', () => { let component: ResultSbomComponent; @@ -21,23 +20,18 @@ describe('ResultSbomComponent (inline template)', () => { }; const mockedSbomDigest = 'sha256:052240e8190b7057439d2bee1dffb9b37c8800e5c1af349f667635ae1debf8f3'; + const mockScanner = { + name: 'Trivy', + vendor: 'vm', + version: 'v1.2', + }; const mockedSbomOverview = { report_id: '12345', scan_status: 'Error', - scanner: { - name: 'Trivy', - vendor: 'vm', - version: 'v1.2', - }, }; const mockedCloneSbomOverview = { report_id: '12346', scan_status: 'Pending', - scanner: { - name: 'Trivy', - vendor: 'vm', - version: 'v1.2', - }, }; const FakedScanService = { scanArtifact: () => of({}), @@ -120,6 +114,7 @@ describe('ResultSbomComponent (inline template)', () => { fixture = TestBed.createComponent(ResultSbomComponent); component = fixture.componentInstance; component.repoName = 'mockRepo'; + component.inputScanner = mockScanner; component.artifactDigest = mockedSbomDigest; component.sbomDigest = mockedSbomDigest; component.sbomOverview = mockData; @@ -180,9 +175,11 @@ describe('ResultSbomComponent (inline template)', () => { }); it('Test ResultSbomComponent getScanner', () => { fixture.detectChanges(); + component.inputScanner = undefined; expect(component.getScanner()).toBeUndefined(); + component.inputScanner = mockScanner; component.sbomOverview = mockedSbomOverview; - expect(component.getScanner()).toBe(mockedSbomOverview.scanner); + expect(component.getScanner()).toBe(mockScanner); component.projectName = 'test'; component.repoName = 'ui'; component.artifactDigest = 'dg'; @@ -239,7 +236,9 @@ describe('ResultSbomComponent (inline template)', () => { fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); - expect(component.stateCheckTimer).toBeUndefined(); + expect(component.sbomOverview.scan_status).toBe( + SBOM_SCAN_STATUS.SUCCESS + ); }); }); }); diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts index 4cfced2f8..46829dd4f 100644 --- a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-scan.component.ts @@ -8,7 +8,6 @@ import { } from '@angular/core'; import { Subscription, timer } from 'rxjs'; import { finalize } from 'rxjs/operators'; -import { ScannerVo } from '../../../../../shared/services'; import { ErrorHandler } from '../../../../../shared/units/error-handler'; import { clone, @@ -27,6 +26,7 @@ import { ScanService } from '../../../../../../../ng-swagger-gen/services/scan.s import { ScanType } from 'ng-swagger-gen/models'; import { ScanTypes } from '../../../../../shared/entities/shared.const'; import { SBOMOverview } from './sbom-overview'; +import { Scanner } from '../../../../left-side-nav/interrogation-services/scanner/scanner'; const STATE_CHECK_INTERVAL: number = 3000; // 3s const RETRY_TIMES: number = 3; @@ -36,7 +36,7 @@ const RETRY_TIMES: number = 3; styleUrls: ['./scanning.scss'], }) export class ResultSbomComponent implements OnInit, OnDestroy { - @Input() inputScanner: ScannerVo; + @Input() inputScanner: Scanner; @Input() repoName: string = ''; @Input() projectName: string = ''; @Input() projectId: string = ''; @@ -176,9 +176,9 @@ export class ResultSbomComponent implements OnInit, OnDestroy { projectName: this.projectName, reference: this.artifactDigest, repositoryName: dbEncodeURIComponent(this.repoName), - // scanType: { - // scan_type: ScanTypes.SBOM, - // }, + scanType: { + scan_type: ScanTypes.SBOM, + }, }) .pipe(finalize(() => this.submitFinish.emit(false))) .subscribe( @@ -219,15 +219,15 @@ export class ResultSbomComponent implements OnInit, OnDestroy { projectName: this.projectName, repositoryName: dbEncodeURIComponent(this.repoName), reference: this.artifactDigest, - // withSbomOverview: true, + withSbomOverview: true, XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES, }) .subscribe( (artifact: Artifact) => { // To keep the same summary reference, use value copy. - // if (artifact.sbom_overview) { - // this.copyValue(artifact.sbom_overview); - // } + if (artifact.sbom_overview) { + this.copyValue(artifact.sbom_overview); + } if (!this.queued && !this.generating) { // Scanning should be done if (this.stateCheckTimer) { @@ -271,10 +271,7 @@ export class ResultSbomComponent implements OnInit, OnDestroy { }/scan/${this.sbomOverview.report_id}/log`; } - getScanner(): ScannerVo { - if (this.sbomOverview && this.sbomOverview.scanner) { - return this.sbomOverview.scanner; - } + getScanner(): Scanner { return this.inputScanner; } diff --git a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts index 2e948442b..f75050dc1 100644 --- a/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/sbom-scanning/sbom-tip-histogram/sbom-tip-histogram.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { ScannerVo, SbomSummary } from '../../../../../../shared/services'; +import { SbomSummary } from '../../../../../../shared/services'; import { SBOM_SCAN_STATUS } from '../../../../../../shared/units/utils'; import { UN_LOGGED_PARAM, @@ -9,6 +9,7 @@ import { } from '../../../../../../account/sign-in/sign-in.service'; import { HAS_STYLE_MODE, StyleMode } from '../../../../../../services/theme'; import { ScanTypes } from '../../../../../../shared/entities/shared.const'; +import { Scanner } from '../../../../../left-side-nav/interrogation-services/scanner/scanner'; const MIN = 60; const MIN_STR = 'min '; @@ -21,7 +22,7 @@ const SUCCESS_PCT: number = 100; styleUrls: ['./sbom-tip-histogram.component.scss'], }) export class SbomTipHistogramComponent { - @Input() scanner: ScannerVo; + @Input() scanner: Scanner; @Input() sbomSummary: SbomSummary = { scan_status: SBOM_SCAN_STATUS.NOT_GENERATED_SBOM, }; @@ -54,6 +55,7 @@ export class SbomTipHistogramComponent { ? `100%` : '0%'; } + isLimitedSuccess(): boolean { return ( this.sbomSummary && this.sbomSummary.complete_percent < SUCCESS_PCT diff --git a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts index 9f840d3ec..66534a82b 100644 --- a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.spec.ts @@ -256,8 +256,8 @@ describe('ResultBarChartComponent (inline template)', () => { }); it('Test ResultBarChartComponent getSummary', () => { fixture.detectChanges(); - // component.summary.scan_status = VULNERABILITY_SCAN_STATUS.SUCCESS; component.getSummary(); + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.SUCCESS; fixture.detectChanges(); fixture.whenStable().then(() => { fixture.detectChanges(); diff --git a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts index ceb8e25b2..ceef85bbe 100644 --- a/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/vulnerability-scanning/result-bar-chart.component.ts @@ -173,9 +173,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy { projectName: this.projectName, reference: this.artifactDigest, repositoryName: dbEncodeURIComponent(this.repoName), - // scanType: { - // scan_type: ScanTypes.VULNERABILITY, - // }, + scanType: { + scan_type: ScanTypes.VULNERABILITY, + }, }) .pipe(finalize(() => this.submitFinish.emit(false))) .subscribe( diff --git a/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts b/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts index 5aa9f8939..c67761e2a 100644 --- a/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts +++ b/src/portal/src/app/services/routing-resolvers/artifact-detail-routing-resolver.service.ts @@ -51,7 +51,7 @@ export class ArtifactDetailRoutingResolverService { projectName: project.name, withLabel: true, withScanOverview: true, - // withSbomOverview: true, + withSbomOverview: true, withTag: false, withImmutableStatus: true, }), diff --git a/src/portal/src/app/shared/shared.module.ts b/src/portal/src/app/shared/shared.module.ts index 5409a12e5..b64995e7a 100644 --- a/src/portal/src/app/shared/shared.module.ts +++ b/src/portal/src/app/shared/shared.module.ts @@ -127,6 +127,23 @@ ClarityIcons.add({ 21.18,0,0,0,4,21.42,21,21,0,0,0,7.71,33.58a1,1,0,0,0,.81.42h19a1,1,0,0,0, .81-.42A21,21,0,0,0,32,21.42,21.18,21.18,0,0,0,29.1,10.49Z"/> `, + sbom: ` + + + + + + + + + + + + +SBOM + + +`, }); @NgModule({ diff --git a/src/portal/src/app/shared/units/utils.ts b/src/portal/src/app/shared/units/utils.ts index 4efa2eadb..1a7a91df7 100644 --- a/src/portal/src/app/shared/units/utils.ts +++ b/src/portal/src/app/shared/units/utils.ts @@ -252,6 +252,18 @@ export const DEFAULT_PAGE_SIZE: number = 15; */ export const DEFAULT_SUPPORTED_MIME_TYPES = 'application/vnd.security.vulnerability.report; version=1.1, application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0'; +/** + * The default supported mime type for SBOM + */ +export const DEFAULT_SBOM_SUPPORTED_MIME_TYPES = + 'application/vnd.security.sbom.report+json; version=1.0'; +/** + * The SBOM supported additional mime types + */ +export const SBOM_SUPPORTED_ADDITIONAL_MIME_TYPES = [ + 'application/spdx+json', + // 'application/vnd.cyclonedx+json', // feature release +]; /** * the property name of vulnerability database updated time @@ -483,6 +495,26 @@ export function downloadFile(fileData) { a.remove(); } +/** + * Download the Json Object as a Json file to local. + * @param data Json Object + * @param filename Json filename + */ +export function downloadJson(data, filename) { + const blob = new Blob([JSON.stringify(data)], { + type: 'application/json;charset=utf-8', + }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + document.body.appendChild(a); + a.setAttribute('style', 'display: none'); + a.href = url; + a.download = filename; + a.click(); + window.URL.revokeObjectURL(url); + a.remove(); +} + export function getChanges( original: any, afterChange: any @@ -1030,6 +1062,7 @@ export enum PageSizeMapKeys { ARTIFACT_LIST_TAB_COMPONENT = 'ArtifactListTabComponent', ARTIFACT_TAGS_COMPONENT = 'ArtifactTagComponent', ARTIFACT_VUL_COMPONENT = 'ArtifactVulnerabilitiesComponent', + ARTIFACT_SBOM_COMPONENT = 'ArtifactSbomComponent', MEMBER_COMPONENT = 'MemberComponent', LABEL_COMPONENT = 'LabelComponent', P2P_POLICY_COMPONENT = 'P2pPolicyComponent', diff --git a/src/portal/src/i18n/lang/de-de-lang.json b/src/portal/src/i18n/lang/de-de-lang.json index cae7b7af0..eb640c0ba 100644 --- a/src/portal/src/i18n/lang/de-de-lang.json +++ b/src/portal/src/i18n/lang/de-de-lang.json @@ -804,6 +804,7 @@ "FILTER_BY_LABEL": "Images nach Label filtern", "FILTER_ARTIFACT_BY_LABEL": "Artefakte nach Label filtern", "ADD_LABELS": "Label hinzufügen", + "STOP": "Stop", "RETAG": "Kopieren", "ACTION": "AKTION", "DEPLOY": "Bereitstellen", @@ -1057,7 +1058,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1107,7 +1108,7 @@ "PLACEHOLDER": "Filter Schwachstellen", "PACKAGE": "Paket", "PACKAGES": "Pakete", - "SCAN_NOW": "Scan", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "SCAN DURCH {{scanner}}", "REPORTED_BY": "GEMELDET VON {{scanner}}", "NO_SCANNER": "KEIN SCANNER", diff --git a/src/portal/src/i18n/lang/en-us-lang.json b/src/portal/src/i18n/lang/en-us-lang.json index 93b802401..27897783a 100644 --- a/src/portal/src/i18n/lang/en-us-lang.json +++ b/src/portal/src/i18n/lang/en-us-lang.json @@ -805,6 +805,7 @@ "FILTER_BY_LABEL": "Filter images by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "ADD_LABELS": "Add Labels", + "STOP": "Stop", "RETAG": "Copy", "ACTION": "ACTION", "DEPLOY": "DEPLOY", @@ -1058,7 +1059,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM ", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1108,7 +1109,7 @@ "PLACEHOLDER": "Filter Vulnerabilities", "PACKAGE": "package", "PACKAGES": "packages", - "SCAN_NOW": "Scan", + "SCAN_NOW": "Scan vulnerability ", "SCAN_BY": "SCAN BY {{scanner}}", "REPORTED_BY": "Reported by {{scanner}}", "NO_SCANNER": "NO SCANNER", diff --git a/src/portal/src/i18n/lang/es-es-lang.json b/src/portal/src/i18n/lang/es-es-lang.json index 11dc5b0b5..0605cdeaa 100644 --- a/src/portal/src/i18n/lang/es-es-lang.json +++ b/src/portal/src/i18n/lang/es-es-lang.json @@ -805,6 +805,7 @@ "FILTER_BY_LABEL": "Filter images by label", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "ADD_LABELS": "Add Labels", + "STOP": "Stop", "RETAG": "Copy", "ACTION": "ACTION", "DEPLOY": "DEPLOY", @@ -1056,7 +1057,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1106,7 +1107,7 @@ "PLACEHOLDER": "Filter Vulnerabilities", "PACKAGE": "package", "PACKAGES": "packages", - "SCAN_NOW": "Scan", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "SCAN BY {{scanner}}", "REPORTED_BY": "Reported by {{scanner}}", "NO_SCANNER": "NO SCANNER", diff --git a/src/portal/src/i18n/lang/fr-fr-lang.json b/src/portal/src/i18n/lang/fr-fr-lang.json index bf45e5c53..9e4004300 100644 --- a/src/portal/src/i18n/lang/fr-fr-lang.json +++ b/src/portal/src/i18n/lang/fr-fr-lang.json @@ -804,6 +804,7 @@ "FILTER_BY_LABEL": "Filtrer les images par label", "FILTER_ARTIFACT_BY_LABEL": "Filtrer les artefact par label", "ADD_LABELS": "Ajouter des labels", + "STOP": "Stop", "RETAG": "Copier", "ACTION": "Action", "DEPLOY": "Déployer", @@ -1056,7 +1057,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1106,12 +1107,12 @@ "PLACEHOLDER": "Filtrer les vulnérabilités", "PACKAGE": "paquet", "PACKAGES": "paquets", - "SCAN_NOW": "Analyser", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "Scan par {{scanner}}", "REPORTED_BY": "Rapporté par {{scanner}}", "NO_SCANNER": "Aucun scanneur", "TRIGGER_STOP_SUCCESS": "Déclenchement avec succès de l'arrêt d'analyse", - "STOP_NOW": "Arrêter l'analyse" + "STOP_NOW": "Stop Scan" }, "PUSH_IMAGE": { "TITLE": "Commande de push", diff --git a/src/portal/src/i18n/lang/ko-kr-lang.json b/src/portal/src/i18n/lang/ko-kr-lang.json index 49f16e841..9ce8e1a17 100644 --- a/src/portal/src/i18n/lang/ko-kr-lang.json +++ b/src/portal/src/i18n/lang/ko-kr-lang.json @@ -802,6 +802,7 @@ "FILTER_BY_LABEL": "라벨별로 이미지 필터", "FILTER_ARTIFACT_BY_LABEL": "라벨별로 아티팩트 필터", "ADD_LABELS": "라벨 추가", + "STOP": "Stop", "RETAG": "복사", "ACTION": "동작", "DEPLOY": "배포", @@ -1055,7 +1056,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1105,12 +1106,12 @@ "PLACEHOLDER": "취약점 필터", "PACKAGE": "패키지", "PACKAGES": "패키지들", - "SCAN_NOW": "스캔", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "{{scanner}로 스캔", "REPORTED_BY": "{{scanner}}로 보고 됨", "NO_SCANNER": "스캐너 없음", "TRIGGER_STOP_SUCCESS": "트리거 중지 스캔이 성공적으로 수행되었습니다", - "STOP_NOW": "스캔 중지" + "STOP_NOW": "Stop Scan" }, "PUSH_IMAGE": { "TITLE": "푸시 명령어", diff --git a/src/portal/src/i18n/lang/pt-br-lang.json b/src/portal/src/i18n/lang/pt-br-lang.json index 2ba78f122..1e4431b0f 100644 --- a/src/portal/src/i18n/lang/pt-br-lang.json +++ b/src/portal/src/i18n/lang/pt-br-lang.json @@ -803,6 +803,7 @@ "FILTER_BY_LABEL": "Filtrar imagens por marcadores", "FILTER_ARTIFACT_BY_LABEL": "Filtrar por marcador", "ADD_LABELS": "Adicionar Marcadores", + "STOP": "Stop", "RETAG": "Copiar", "ACTION": "AÇÃO", "DEPLOY": "IMPLANTAR", @@ -1054,7 +1055,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1104,12 +1105,12 @@ "PLACEHOLDER": "Filtrar", "PACKAGE": "pacote", "PACKAGES": "pacotes", - "SCAN_NOW": "Examinar", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "EXAMINAR COM {{scanner}}", "REPORTED_BY": "Encontrado com {{scanner}}", "NO_SCANNER": "NENHUM", "TRIGGER_STOP_SUCCESS": "Exame foi interrompido", - "STOP_NOW": "Interromper" + "STOP_NOW": "Stop Scan" }, "PUSH_IMAGE": { "TITLE": "Comando Push", diff --git a/src/portal/src/i18n/lang/tr-tr-lang.json b/src/portal/src/i18n/lang/tr-tr-lang.json index 0dddab935..3ac8e7c46 100644 --- a/src/portal/src/i18n/lang/tr-tr-lang.json +++ b/src/portal/src/i18n/lang/tr-tr-lang.json @@ -804,6 +804,7 @@ "FILTER_BY_LABEL": "İmajları etikete göre filtrele", "FILTER_ARTIFACT_BY_LABEL": "Filter actifact by label", "ADD_LABELS": "Etiketler Ekle", + "STOP": "Stop", "RETAG": "Copy", "ACTION": "AKSİYON", "DEPLOY": "YÜKLE", @@ -1057,7 +1058,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1107,7 +1108,7 @@ "PLACEHOLDER": "Güvenlik Açıklarını Filtrele", "PACKAGE": "paket", "PACKAGES": "paketler", - "SCAN_NOW": "Tara", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "SCAN BY {{scanner}}", "REPORTED_BY": "Reported by {{scanner}}", "NO_SCANNER": "NO SCANNER", diff --git a/src/portal/src/i18n/lang/zh-cn-lang.json b/src/portal/src/i18n/lang/zh-cn-lang.json index bb2a58578..c8b0a4258 100644 --- a/src/portal/src/i18n/lang/zh-cn-lang.json +++ b/src/portal/src/i18n/lang/zh-cn-lang.json @@ -801,6 +801,7 @@ "LABELS": "标签", "ADD_LABEL_TO_IMAGE": "添加标签到此镜像", "ADD_LABELS": "添加标签", + "STOP": "Stop", "RETAG": "拷贝", "FILTER_BY_LABEL": "过滤标签", "FILTER_ARTIFACT_BY_LABEL": "通过标签过滤Artifact", @@ -1055,7 +1056,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", @@ -1105,7 +1106,7 @@ "PLACEHOLDER": "过滤漏洞", "PACKAGE": "组件", "PACKAGES": "组件", - "SCAN_NOW": "扫描", + "SCAN_NOW": "Scan vulnerability", "SCAN_BY": "使用 {{scanner}} 进行扫描", "REPORTED_BY": "结果由 {{scanner}} 提供", "NO_SCANNER": "无扫描器", diff --git a/src/portal/src/i18n/lang/zh-tw-lang.json b/src/portal/src/i18n/lang/zh-tw-lang.json index bc1732775..ca8c90c44 100644 --- a/src/portal/src/i18n/lang/zh-tw-lang.json +++ b/src/portal/src/i18n/lang/zh-tw-lang.json @@ -801,6 +801,7 @@ "LABELS": "標籤", "ADD_LABEL_TO_IMAGE": "新增標籤到此映像檔", "ADD_LABELS": "新增標籤", + "STOP": "Stop", "RETAG": "複製", "FILTER_BY_LABEL": "篩選標籤", "FILTER_ARTIFACT_BY_LABEL": "透過標籤篩選 Artifact", @@ -1054,7 +1055,7 @@ "NO_SBOM": "No SBOM", "PACKAGES": "SBOM", "REPORTED_BY": "Reported by {{scanner}}", - "GENERATE": "Create SBOM", + "GENERATE": "Generate SBOM", "DOWNLOAD": "Download SBOM", "Details": "SBOM details", "STOP": "Stop SBOM", From 9c3fc28250b9d7d3956b7395367016ab9c6a79e3 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Fri, 19 Apr 2024 10:14:28 +0800 Subject: [PATCH 030/100] Allow generate sbom in proxy cache project (#20298) Signed-off-by: stonezdj --- src/server/middleware/repoproxy/proxy.go | 14 +++++++--- src/server/middleware/repoproxy/proxy_test.go | 28 +++++++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/server/middleware/repoproxy/proxy.go b/src/server/middleware/repoproxy/proxy.go index 5fb44a681..ddb201784 100644 --- a/src/server/middleware/repoproxy/proxy.go +++ b/src/server/middleware/repoproxy/proxy.go @@ -28,6 +28,7 @@ import ( "github.com/goharbor/harbor/src/controller/proxy" "github.com/goharbor/harbor/src/controller/registry" "github.com/goharbor/harbor/src/lib" + "github.com/goharbor/harbor/src/lib/config" "github.com/goharbor/harbor/src/lib/errors" httpLib "github.com/goharbor/harbor/src/lib/http" "github.com/goharbor/harbor/src/lib/log" @@ -259,16 +260,21 @@ func setHeaders(w http.ResponseWriter, size int64, mediaType string, dig string) } // isProxySession check if current security context is proxy session -func isProxySession(ctx context.Context) bool { +func isProxySession(ctx context.Context, projectName string) bool { sc, ok := security.FromContext(ctx) if !ok { log.Error("Failed to get security context") return false } - if sc.GetUsername() == proxycachesecret.ProxyCacheService { + username := sc.GetUsername() + if username == proxycachesecret.ProxyCacheService { return true } - return false + // it should include the auto generate SBOM session, so that it could generate SBOM accessory in proxy cache project + robotPrefix := config.RobotPrefix(ctx) + scannerPrefix := config.ScannerRobotPrefix(ctx) + prefix := fmt.Sprintf("%s%s+%s", robotPrefix, projectName, scannerPrefix) + return strings.HasPrefix(username, prefix) } // DisableBlobAndManifestUploadMiddleware disable push artifact to a proxy project with a non-proxy session @@ -281,7 +287,7 @@ func DisableBlobAndManifestUploadMiddleware() func(http.Handler) http.Handler { httpLib.SendError(w, err) return } - if p.IsProxy() && !isProxySession(ctx) { + if p.IsProxy() && !isProxySession(ctx, art.ProjectName) { httpLib.SendError(w, errors.DeniedError( errors.Errorf("can not push artifact to a proxy project: %v", p.Name))) diff --git a/src/server/middleware/repoproxy/proxy_test.go b/src/server/middleware/repoproxy/proxy_test.go index 4b7ee3d75..c47f20db0 100644 --- a/src/server/middleware/repoproxy/proxy_test.go +++ b/src/server/middleware/repoproxy/proxy_test.go @@ -18,7 +18,9 @@ import ( "context" "testing" + "github.com/goharbor/harbor/src/common/models" "github.com/goharbor/harbor/src/common/security" + "github.com/goharbor/harbor/src/common/security/local" "github.com/goharbor/harbor/src/common/security/proxycachesecret" securitySecret "github.com/goharbor/harbor/src/common/security/secret" ) @@ -29,6 +31,19 @@ func TestIsProxySession(t *testing.T) { sc2 := proxycachesecret.NewSecurityContext("library/hello-world") proxyCtx := security.NewContext(context.Background(), sc2) + + user := &models.User{ + Username: "robot$library+scanner-8ec3b47a-fd29-11ee-9681-0242c0a87009", + } + userSc := local.NewSecurityContext(user) + scannerCtx := security.NewContext(context.Background(), userSc) + + otherRobot := &models.User{ + Username: "robot$library+test-8ec3b47a-fd29-11ee-9681-0242c0a87009", + } + userSc2 := local.NewSecurityContext(otherRobot) + nonScannerCtx := security.NewContext(context.Background(), userSc2) + cases := []struct { name string in context.Context @@ -44,15 +59,24 @@ func TestIsProxySession(t *testing.T) { in: proxyCtx, want: true, }, + { + name: `robot account`, + in: scannerCtx, + want: true, + }, + { + name: `non scanner robot`, + in: nonScannerCtx, + want: false, + }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { - got := isProxySession(tt.in) + got := isProxySession(tt.in, "library") if got != tt.want { t.Errorf(`(%v) = %v; want "%v"`, tt.in, got, tt.want) } - }) } } From b3dc183f476b20c5e51e49103230ef5151aa477b Mon Sep 17 00:00:00 2001 From: Lichao Xue <68891670+xuelichao@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:01:54 +0800 Subject: [PATCH 031/100] Fixed an issue where the scan stop button can only be clicked once (#20302) Signed-off-by: xuelichao --- .../artifact-list-tab/artifact-list-tab.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts index f73b448bb..d880f8856 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.ts @@ -907,7 +907,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { this.scanStoppedArtifactLength += 1; // all selected scan action has stopped if (this.scanStoppedArtifactLength === this.onStopScanArtifactsLength) { - this.onSendingScanCommand = e; + this.onSendingStopScanCommand = e; } } @@ -923,7 +923,7 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy { this.sbomStoppedArtifactLength += 1; // all selected scan action has stopped if (this.sbomStoppedArtifactLength === this.onStopSbomArtifactsLength) { - this.onSendingSbomCommand = e; + this.onSendingStopSbomCommand = e; } } From 0d9dc4b4a473e682d1d9de00394865659df7aca8 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Fri, 19 Apr 2024 15:36:56 +0800 Subject: [PATCH 032/100] Add enableCapabilities to extraAttrs for stop (#20299) Signed-off-by: stonezdj --- src/controller/scan/base_controller.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index 52eb4eefe..c1b68947c 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -70,10 +70,11 @@ const ( artfiactKey = "artifact" registrationKey = "registration" - artifactIDKey = "artifact_id" - artifactTagKey = "artifact_tag" - reportUUIDsKey = "report_uuids" - robotIDKey = "robot_id" + artifactIDKey = "artifact_id" + artifactTagKey = "artifact_tag" + reportUUIDsKey = "report_uuids" + robotIDKey = "robot_id" + enabledCapabilities = "enabled_capabilities" ) // uuidGenerator is a func template which is for generating UUID. @@ -317,6 +318,9 @@ func (bc *basicController) Scan(ctx context.Context, artifact *ar.Artifact, opti "id": r.ID, "name": r.Name, }, + enabledCapabilities: map[string]interface{}{ + "type": opts.GetScanType(), + }, } if op := operator.FromContext(ctx); op != "" { extraAttrs["operator"] = op From d7594298310f81053d1d63d42f47d0ffc4053bbb Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Sat, 20 Apr 2024 10:37:30 +0800 Subject: [PATCH 033/100] Set default capability for old scanners (#20306) Signed-off-by: stonezdj Co-authored-by: Wang Yan --- src/pkg/scan/rest/v1/models.go | 12 ++++++++++-- src/pkg/scan/rest/v1/models_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/pkg/scan/rest/v1/models.go b/src/pkg/scan/rest/v1/models.go index 21352c749..9c25c16ea 100644 --- a/src/pkg/scan/rest/v1/models.go +++ b/src/pkg/scan/rest/v1/models.go @@ -161,14 +161,22 @@ func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapabil // ConvertCapability converts the capability to map, used in get scanner API func (md *ScannerAdapterMetadata) ConvertCapability() map[string]interface{} { capabilities := make(map[string]interface{}) + oldScanner := true for _, c := range md.Capabilities { + if len(c.Type) > 0 { + oldScanner = false + } if c.Type == ScanTypeVulnerability { capabilities[supportVulnerability] = true - } - if c.Type == ScanTypeSbom { + } else if c.Type == ScanTypeSbom { capabilities[supportSBOM] = true } } + if oldScanner && len(capabilities) == 0 { + // to compatible with old version scanner, suppose they should always support scan vulnerability when capability is empty + capabilities[supportVulnerability] = true + capabilities[supportSBOM] = false + } return capabilities } diff --git a/src/pkg/scan/rest/v1/models_test.go b/src/pkg/scan/rest/v1/models_test.go index e96aa0178..96590bc4c 100644 --- a/src/pkg/scan/rest/v1/models_test.go +++ b/src/pkg/scan/rest/v1/models_test.go @@ -13,3 +13,29 @@ func TestIsSupportedMimeType(t *testing.T) { // Test with an unsupported mime type assert.False(t, isSupportedMimeType("unsupported/mime-type"), "isSupportedMimeType should return false for unsupported mime types") } + +func TestConvertCapability(t *testing.T) { + md := &ScannerAdapterMetadata{ + Capabilities: []*ScannerCapability{ + {Type: ScanTypeSbom}, + {Type: ScanTypeVulnerability}, + }, + } + result := md.ConvertCapability() + assert.Equal(t, result[supportSBOM], true) + assert.Equal(t, result[supportVulnerability], true) +} + +func TestConvertCapabilityOldScaner(t *testing.T) { + md := &ScannerAdapterMetadata{ + Capabilities: []*ScannerCapability{ + { + ConsumesMimeTypes: []string{"application/vnd.oci.image.manifest.v1+json", "application/vnd.docker.distribution.manifest.v2+json"}, + ProducesMimeTypes: []string{MimeTypeNativeReport}, + }, + }, + } + result := md.ConvertCapability() + assert.Equal(t, result[supportSBOM], false) + assert.Equal(t, result[supportVulnerability], true) +} From e7fce627238addf5e7efae95655cc47571b147d6 Mon Sep 17 00:00:00 2001 From: Lichao Xue <68891670+xuelichao@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:29:48 +0800 Subject: [PATCH 034/100] Wrong values shown for the columns of support_sbom and support_vulnerability in scanner list (#20308) Fix wrong value shown for the columns of support_sbom and support_vulnerability in scanner list Signed-off-by: xuelichao --- .../scanner/config-scanner.component.ts | 8 +-- .../scanner-metadata/scanner-metadata.html | 53 +++++++++++++------ src/portal/src/i18n/lang/de-de-lang.json | 3 +- src/portal/src/i18n/lang/en-us-lang.json | 3 +- src/portal/src/i18n/lang/es-es-lang.json | 3 +- src/portal/src/i18n/lang/fr-fr-lang.json | 3 +- src/portal/src/i18n/lang/ko-kr-lang.json | 3 +- src/portal/src/i18n/lang/pt-br-lang.json | 3 +- src/portal/src/i18n/lang/tr-tr-lang.json | 3 +- src/portal/src/i18n/lang/zh-cn-lang.json | 3 +- src/portal/src/i18n/lang/zh-tw-lang.json | 3 +- 11 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/config-scanner.component.ts b/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/config-scanner.component.ts index 550aff8bc..b05ce3657 100644 --- a/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/config-scanner.component.ts +++ b/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/config-scanner.component.ts @@ -180,12 +180,8 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy { } supportCapability(scanner: Scanner, capabilityType: string): boolean { - return scanner && scanner.metadata && capabilityType - ? ( - scanner?.metadata?.capabilities?.filter( - ({ type }) => type === capabilityType - ) ?? [] - ).length >= 1 + return scanner && scanner.capabilities && capabilityType + ? scanner?.capabilities?.[`support_${capabilityType}`] ?? false : false; } diff --git a/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/scanner-metadata/scanner-metadata.html b/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/scanner-metadata/scanner-metadata.html index eeefb4aca..4da2dd94b 100644 --- a/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/scanner-metadata/scanner-metadata.html +++ b/src/portal/src/app/base/left-side-nav/interrogation-services/scanner/scanner-metadata/scanner-metadata.html @@ -19,21 +19,44 @@
{{ 'SCANNER.CAPABILITIES' | translate }}
-
- {{ 'SCANNER.CONSUMES_MIME_TYPES_COLON' | translate }} - -
-
- {{ 'SCANNER.PRODUCTS_MIME_TYPES_COLON' | translate }} - +
+ {{ i }}: +
+ {{ 'SCANNER.CONSUMES_MIME_TYPES_COLON' | translate }} + +
+
+ {{ 'SCANNER.PRODUCTS_MIME_TYPES_COLON' | translate }} + +
+
+ {{ 'SCANNER.CAPABILITIES_TYPE' | translate }} + + {{ + (scannerMetadata?.capabilities[i]?.type === 'sbom' + ? 'SCANNER.SBOM' + : scannerMetadata?.capabilities[i]?.type === + 'vulnerability' + ? 'SCANNER.VULNERABILITY' + : scannerMetadata?.capabilities[i]?.type + ) | translate + }} + +
{{ 'SCANNER.PROPERTIES' | translate }}
Date: Mon, 22 Apr 2024 14:36:35 +0800 Subject: [PATCH 035/100] feat: add tc for limited guest of a project to get repository (#20311) Signed-off-by: Shengwen Yu --- .gitignore | 2 + .../test_user_limited_guest_get_repository.py | 62 +++++++++++++++++++ tests/robot-cases/Group0-BAT/API_DB.robot | 4 ++ 3 files changed, 68 insertions(+) create mode 100644 tests/apitests/python/test_user_limited_guest_get_repository.py diff --git a/.gitignore b/.gitignore index ef34ce1bf..f2b08ad4a 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ src/server/v2.0/models/ src/server/v2.0/restapi/ .editorconfig +harborclient/ +openapi-generator-cli.jar diff --git a/tests/apitests/python/test_user_limited_guest_get_repository.py b/tests/apitests/python/test_user_limited_guest_get_repository.py new file mode 100644 index 000000000..d29b936fa --- /dev/null +++ b/tests/apitests/python/test_user_limited_guest_get_repository.py @@ -0,0 +1,62 @@ +from __future__ import absolute_import +import unittest + + +from testutils import ADMIN_CLIENT, suppress_urllib3_warning +from testutils import harbor_server +from testutils import admin_user +from testutils import admin_pwd +from testutils import created_project +from testutils import created_user +from testutils import TEARDOWN +from library.repository import push_self_build_image_to_project +from library.repository import Repository + + +class TestLimitedGuestGetRepository(unittest.TestCase): + + + @suppress_urllib3_warning + def setUp(self): + self.repository = Repository() + + @unittest.skipIf(TEARDOWN == False, "Test data won't be erased.") + def tearDown(self): + print("Case completed") + + def testLimitedGuestGetRepository(self): + """ + Test case: + Limited Guest GetRepository + Test step and expected result: + 1. Create a new user(UA) + 2. Create a private project(PA) + 3. Add (UA) as "Limited Guest" to this (PA) + 4. Push an image to project(PA) + 5. Call the "GetRepository" API, it should return 200 status code and project_id should be as expected, and the name should be "ProjectName/ImageName" + 6. Delete repository(RA) + """ + url = ADMIN_CLIENT["endpoint"] + user_001_password = "Aa123456" + # 1. Create a new user(UA) + with created_user(user_001_password) as (user_id, user_name): + #2. Create a new private project(PA) by user(UA); + #3. Add user(UA) as a member of project(PA) with "Limited Guest" role; + with created_project(metadata={"public": "false"}, user_id=user_id, member_role_id=5) as (project_id, project_name): + #4. Push an image to project(PA) by user(UA), then check the project quota usage; + image, tag = "goharbor/alpine", "3.10" + push_self_build_image_to_project(project_name, harbor_server, admin_user, admin_pwd, image, tag) + + #5. Call the "GetRepository" API, it should return 200 status code and the "name" attribute is "ProjectName/ImageName" + USER_CLIENT=dict(endpoint=url, username=user_name, password=user_001_password) + repository_data = self.repository.get_repository(project_name, "goharbor%2Falpine", **USER_CLIENT) + self.assertEqual(repository_data.project_id, project_id) + self.assertEqual(repository_data.name, project_name + "/" + image) + + #6. Delete repository(RA) + self.repository.delete_repository(project_name, "goharbor%2Falpine", **ADMIN_CLIENT) + + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/robot-cases/Group0-BAT/API_DB.robot b/tests/robot-cases/Group0-BAT/API_DB.robot index 179d85ebb..f1a92b720 100644 --- a/tests/robot-cases/Group0-BAT/API_DB.robot +++ b/tests/robot-cases/Group0-BAT/API_DB.robot @@ -210,3 +210,7 @@ Test Case - Banner Message Test Case - User CRUD [Tags] user_crud Harbor API Test ./tests/apitests/python/test_user_crud.py + +Test Case - Limited Guest GetRepository + [Tags] limited_guest_getrepository + Harbor API Test ./tests/apitests/python/test_user_limited_guest_get_repository.py From ea3cd06171c10f20d5b942fd7de40107c26843c2 Mon Sep 17 00:00:00 2001 From: MinerYang Date: Mon, 22 Apr 2024 16:34:08 +0800 Subject: [PATCH 036/100] add prepare migration script for 2.11.0 (#20315) Signed-off-by: yminer correct jaeger agent_host update ip_family part --- make/harbor.yml.tmpl | 2 +- make/photon/prepare/commands/migrate.py | 2 +- make/photon/prepare/migrations/__init__.py | 2 +- .../migrations/version_2_11_0/__init__.py | 21 + .../version_2_11_0/harbor.yml.jinja | 737 ++++++++++++++++++ 5 files changed, 761 insertions(+), 3 deletions(-) create mode 100644 make/photon/prepare/migrations/version_2_11_0/__init__.py create mode 100644 make/photon/prepare/migrations/version_2_11_0/harbor.yml.jinja diff --git a/make/harbor.yml.tmpl b/make/harbor.yml.tmpl index 22e421691..e81abfc43 100644 --- a/make/harbor.yml.tmpl +++ b/make/harbor.yml.tmpl @@ -174,7 +174,7 @@ log: # port: 5140 #This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY! -_version: 2.10.0 +_version: 2.11.0 # Uncomment external_database if using external database. # external_database: diff --git a/make/photon/prepare/commands/migrate.py b/make/photon/prepare/commands/migrate.py index 6808aa18f..6ec8d7a28 100644 --- a/make/photon/prepare/commands/migrate.py +++ b/make/photon/prepare/commands/migrate.py @@ -10,7 +10,7 @@ from migrations import accept_versions @click.command() @click.option('-i', '--input', 'input_', required=True, help="The path of original config file") @click.option('-o', '--output', default='', help="the path of output config file") -@click.option('-t', '--target', default='2.10.0', help="target version of input path") +@click.option('-t', '--target', default='2.11.0', help="target version of input path") def migrate(input_, output, target): """ migrate command will migrate config file style to specific version diff --git a/make/photon/prepare/migrations/__init__.py b/make/photon/prepare/migrations/__init__.py index 4ecb468a3..14b50018a 100644 --- a/make/photon/prepare/migrations/__init__.py +++ b/make/photon/prepare/migrations/__init__.py @@ -2,4 +2,4 @@ import os MIGRATION_BASE_DIR = os.path.dirname(__file__) -accept_versions = {'1.9.0', '1.10.0', '2.0.0', '2.1.0', '2.2.0', '2.3.0', '2.4.0', '2.5.0', '2.6.0', '2.7.0', '2.8.0', '2.9.0','2.10.0'} \ No newline at end of file +accept_versions = {'1.9.0', '1.10.0', '2.0.0', '2.1.0', '2.2.0', '2.3.0', '2.4.0', '2.5.0', '2.6.0', '2.7.0', '2.8.0', '2.9.0','2.10.0', '2.11.0'} \ No newline at end of file diff --git a/make/photon/prepare/migrations/version_2_11_0/__init__.py b/make/photon/prepare/migrations/version_2_11_0/__init__.py new file mode 100644 index 000000000..8f2f64cfa --- /dev/null +++ b/make/photon/prepare/migrations/version_2_11_0/__init__.py @@ -0,0 +1,21 @@ +import os +from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape +from utils.migration import read_conf + +revision = '2.11.0' +down_revisions = ['2.10.0'] + +def migrate(input_cfg, output_cfg): + current_dir = os.path.dirname(__file__) + tpl = Environment( + loader=FileSystemLoader(current_dir), + undefined=StrictUndefined, + trim_blocks=True, + lstrip_blocks=True, + autoescape = select_autoescape() + ).get_template('harbor.yml.jinja') + + config_dict = read_conf(input_cfg) + + with open(output_cfg, 'w') as f: + f.write(tpl.render(**config_dict)) diff --git a/make/photon/prepare/migrations/version_2_11_0/harbor.yml.jinja b/make/photon/prepare/migrations/version_2_11_0/harbor.yml.jinja new file mode 100644 index 000000000..ef0be73db --- /dev/null +++ b/make/photon/prepare/migrations/version_2_11_0/harbor.yml.jinja @@ -0,0 +1,737 @@ +# Configuration file of Harbor + +# The IP address or hostname to access admin UI and registry service. +# DO NOT use localhost or 127.0.0.1, because Harbor needs to be accessed by external clients. +hostname: {{ hostname }} + +# http related config +{% if http is defined %} +http: + # port for http, default is 80. If https enabled, this port will redirect to https port + port: {{ http.port }} +{% else %} +# http: +# # port for http, default is 80. If https enabled, this port will redirect to https port +# port: 80 +{% endif %} + +{% if https is defined %} +# https related config +https: + # https port for harbor, default is 443 + port: {{ https.port }} + # The path of cert and key files for nginx + certificate: {{ https.certificate }} + private_key: {{ https.private_key }} + # enable strong ssl ciphers (default: false) + {% if strong_ssl_ciphers is defined %} + strong_ssl_ciphers: {{ strong_ssl_ciphers | lower }} + {% else %} + strong_ssl_ciphers: false + {% endif %} +{% else %} +# https related config +# https: +# # https port for harbor, default is 443 +# port: 443 +# # The path of cert and key files for nginx +# certificate: /your/certificate/path +# private_key: /your/private/key/path +# enable strong ssl ciphers (default: false) +# strong_ssl_ciphers: false +{% endif %} + +# # Harbor will set ipv4 enabled only by default if this block is not configured +# # Otherwise, please uncomment this block to configure your own ip_family stacks +{% if ip_family is defined %} +ip_family: + # ipv6Enabled set to true if ipv6 is enabled in docker network, currently it affected the nginx related component + {% if ip_family.ipv6 is defined %} + ipv6: + enabled: {{ ip_family.ipv6.enabled | lower }} + {% else %} + ipv6: + enabled: false + {% endif %} + # ipv4Enabled set to true by default, currently it affected the nginx related component + {% if ip_family.ipv4 is defined %} + ipv4: + enabled: {{ ip_family.ipv4.enabled | lower }} + {% else %} + ipv4: + enabled: true + {% endif %} +{% else %} +# ip_family: +# # ipv6Enabled set to true if ipv6 is enabled in docker network, currently it affected the nginx related component +# ipv6: +# enabled: false +# # ipv4Enabled set to true by default, currently it affected the nginx related component +# ipv4: +# enabled: true +{% endif %} + +{% if internal_tls is defined %} +# Uncomment following will enable tls communication between all harbor components +internal_tls: + # set enabled to true means internal tls is enabled + enabled: {{ internal_tls.enabled | lower }} + {% if internal_tls.dir is defined %} + # put your cert and key files on dir + dir: {{ internal_tls.dir }} + {% endif %} +{% else %} +# internal_tls: +# # set enabled to true means internal tls is enabled +# enabled: true +# # put your cert and key files on dir +# dir: /etc/harbor/tls/internal +{% endif %} + +# Uncomment external_url if you want to enable external proxy +# And when it enabled the hostname will no longer used +{% if external_url is defined %} +external_url: {{ external_url }} +{% else %} +# external_url: https://reg.mydomain.com:8433 +{% endif %} + +# The initial password of Harbor admin +# It only works in first time to install harbor +# Remember Change the admin password from UI after launching Harbor. +{% if harbor_admin_password is defined %} +harbor_admin_password: {{ harbor_admin_password }} +{% else %} +harbor_admin_password: Harbor12345 +{% endif %} + +# Harbor DB configuration +database: +{% if database is defined %} + # The password for the root user of Harbor DB. Change this before any production use. + password: {{ database.password}} + # The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained. + max_idle_conns: {{ database.max_idle_conns }} + # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections. + # Note: the default number of connections is 1024 for postgres of harbor. + max_open_conns: {{ database.max_open_conns }} + # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + {% if database.conn_max_lifetime is defined %} + conn_max_lifetime: {{ database.conn_max_lifetime }} + {% else %} + conn_max_lifetime: 5m + {% endif %} + # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + {% if database.conn_max_idle_time is defined %} + conn_max_idle_time: {{ database.conn_max_idle_time }} + {% else %} + conn_max_idle_time: 0 + {% endif %} +{% else %} + # The password for the root user of Harbor DB. Change this before any production use. + password: root123 + # The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained. + max_idle_conns: 100 + # The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections. + # Note: the default number of connections is 1024 for postgres of harbor. + max_open_conns: 900 + # The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's age. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_lifetime: 5m + # The maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If it <= 0, connections are not closed due to a connection's idle time. + # The value is a duration string. A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + conn_max_idle_time: 0 +{% endif %} + +{% if data_volume is defined %} +# The default data volume +data_volume: {{ data_volume }} +{% else %} +# The default data volume +data_volume: /data +{% endif %} + +# Harbor Storage settings by default is using /data dir on local filesystem +# Uncomment storage_service setting If you want to using external storage +{% if storage_service is defined %} +storage_service: + {% for key, value in storage_service.items() %} + {% if key == 'ca_bundle' %} +# # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore +# # of registry's and chart repository's containers. This is usually needed when the user hosts a internal storage with self signed certificate. + ca_bundle: {{ value if value is not none else '' }} + {% elif key == 'redirect' %} +# # set disable to true when you want to disable registry redirect + redirect: + {% if storage_service.redirect.disabled is defined %} + disable: {{ storage_service.redirect.disabled | lower}} + {% else %} + disable: {{ storage_service.redirect.disable | lower}} + {% endif %} + {% else %} +# # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss +# # for more info about this configuration please refer https://distribution.github.io/distribution/about/configuration/ +# # and https://distribution.github.io/distribution/storage-drivers/ + {{ key }}: + {% for k, v in value.items() %} + {{ k }}: {{ v if v is not none else '' }} + {% endfor %} + {% endif %} + {% endfor %} +{% else %} +# storage_service: +# # ca_bundle is the path to the custom root ca certificate, which will be injected into the truststore +# # of registry's and chart repository's containers. This is usually needed when the user hosts a internal storage with self signed certificate. +# ca_bundle: + +# # storage backend, default is filesystem, options include filesystem, azure, gcs, s3, swift and oss +# # for more info about this configuration please refer https://distribution.github.io/distribution/about/configuration/ +# # and https://distribution.github.io/distribution/storage-drivers/ +# filesystem: +# maxthreads: 100 +# # set disable to true when you want to disable registry redirect +# redirect: +# disable: false +{% endif %} + +# Trivy configuration +# +# Trivy DB contains vulnerability information from NVD, Red Hat, and many other upstream vulnerability databases. +# It is downloaded by Trivy from the GitHub release page https://github.com/aquasecurity/trivy-db/releases and cached +# in the local file system. In addition, the database contains the update timestamp so Trivy can detect whether it +# should download a newer version from the Internet or use the cached one. Currently, the database is updated every +# 12 hours and published as a new release to GitHub. +{% if trivy is defined %} +trivy: + # ignoreUnfixed The flag to display only fixed vulnerabilities + {% if trivy.ignore_unfixed is defined %} + ignore_unfixed: {{ trivy.ignore_unfixed | lower }} + {% else %} + ignore_unfixed: false + {% endif %} + # skipUpdate The flag to enable or disable Trivy DB downloads from GitHub + # + # You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues. + # If the flag is enabled you have to download the `trivy-offline.tar.gz` archive manually, extract `trivy.db` and + # `metadata.json` files and mount them in the `/home/scanner/.cache/trivy/db` path. + {% if trivy.skip_update is defined %} + skip_update: {{ trivy.skip_update | lower }} + {% else %} + skip_update: false + {% endif %} + {% if trivy.skip_java_db_update is defined %} + # skipJavaDBUpdate If the flag is enabled you have to manually download the `trivy-java.db` file and mount it in the + # `/home/scanner/.cache/trivy/java-db/trivy-java.db` path + skip_java_db_update: {{ trivy.skip_java_db_update | lower }} + {% else %} + skip_java_db_update: false + {% endif %} + # + {% if trivy.offline_scan is defined %} + offline_scan: {{ trivy.offline_scan | lower }} + {% else %} + offline_scan: false + {% endif %} + # + # Comma-separated list of what security issues to detect. Possible values are `vuln`, `config` and `secret`. Defaults to `vuln`. + {% if trivy.security_check is defined %} + security_check: {{ trivy.security_check }} + {% else %} + security_check: vuln + {% endif %} + # + # insecure The flag to skip verifying registry certificate + {% if trivy.insecure is defined %} + insecure: {{ trivy.insecure | lower }} + {% else %} + insecure: false + {% endif %} + # + {% if trivy.timeout is defined %} + # timeout The duration to wait for scan completion. + # There is upper bound of 30 minutes defined in scan job. So if this `timeout` is larger than 30m0s, it will also timeout at 30m0s. + timeout: {{ trivy.timeout}} + {% else %} + timeout: 5m0s + {% endif %} + # + # github_token The GitHub access token to download Trivy DB + # + # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough + # for production operations. If, for any reason, it's not enough, you could increase the rate limit to 5000 + # requests per hour by specifying the GitHub access token. For more details on GitHub rate limiting please consult + # https://developer.github.com/v3/#rate-limiting + # + # You can create a GitHub token by following the instructions in + # https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line + # + {% if trivy.github_token is defined %} + github_token: {{ trivy.github_token }} + {% else %} + # github_token: xxx + {% endif %} +{% else %} +# trivy: +# # ignoreUnfixed The flag to display only fixed vulnerabilities +# ignore_unfixed: false +# # skipUpdate The flag to enable or disable Trivy DB downloads from GitHub +# # +# # You might want to enable this flag in test or CI/CD environments to avoid GitHub rate limiting issues. +# # If the flag is enabled you have to download the `trivy-offline.tar.gz` archive manually, extract `trivy.db` and +# # `metadata.json` files and mount them in the `/home/scanner/.cache/trivy/db` path. +# skip_update: false +# # +# # skipJavaDBUpdate If the flag is enabled you have to manually download the `trivy-java.db` file and mount it in the +# # `/home/scanner/.cache/trivy/java-db/trivy-java.db` path +# skip_java_db_update: false +# # +# #The offline_scan option prevents Trivy from sending API requests to identify dependencies. +# # Scanning JAR files and pom.xml may require Internet access for better detection, but this option tries to avoid it. +# # For example, the offline mode will not try to resolve transitive dependencies in pom.xml when the dependency doesn't +# # exist in the local repositories. It means a number of detected vulnerabilities might be fewer in offline mode. +# # It would work if all the dependencies are in local. +# # This option doesn’t affect DB download. You need to specify "skip-update" as well as "offline-scan" in an air-gapped environment. +# offline_scan: false +# # +# # insecure The flag to skip verifying registry certificate +# insecure: false +# # github_token The GitHub access token to download Trivy DB +# # +# # Anonymous downloads from GitHub are subject to the limit of 60 requests per hour. Normally such rate limit is enough +# # for production operations. If, for any reason, it's not enough, you could increase the rate limit to 5000 +# # requests per hour by specifying the GitHub access token. For more details on GitHub rate limiting please consult +# # https://developer.github.com/v3/#rate-limiting +# # +# # timeout The duration to wait for scan completion. +# # There is upper bound of 30 minutes defined in scan job. So if this `timeout` is larger than 30m0s, it will also timeout at 30m0s. +# timeout: 5m0s +# # +# # You can create a GitHub token by following the instructions in +# # https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line +# # +# # github_token: xxx +{% endif %} + +jobservice: + # Maximum number of job workers in job service +{% if jobservice is defined %} + max_job_workers: {{ jobservice.max_job_workers }} + # The jobLoggers backend name, only support "STD_OUTPUT", "FILE" and/or "DB" + {% if jobservice.job_loggers is defined %} + job_loggers: + {% for job_logger in jobservice.job_loggers %} + - {{job_logger}} + {% endfor %} + {% else %} + job_loggers: + - STD_OUTPUT + - FILE + # - DB + {% endif %} + # The jobLogger sweeper duration (ignored if `jobLogger` is `stdout`) + {% if jobservice.logger_sweeper_duration is defined %} + logger_sweeper_duration: {{ jobservice.logger_sweeper_duration }} + {% else %} + logger_sweeper_duration: 1 + {% endif %} +{% else %} + max_job_workers: 10 + # The jobLoggers backend name, only support "STD_OUTPUT", "FILE" and/or "DB" + job_loggers: + - STD_OUTPUT + - FILE + # - DB + # The jobLogger sweeper duration (ignored if `jobLogger` is `stdout`) + logger_sweeper_duration: 1 +{% endif %} + +notification: + # Maximum retry count for webhook job +{% if notification is defined %} + webhook_job_max_retry: {{ notification.webhook_job_max_retry}} + # HTTP client timeout for webhook job + {% if notification.webhook_job_http_client_timeout is defined %} + webhook_job_http_client_timeout: {{ notification.webhook_job_http_client_timeout }} + {% else %} + webhook_job_http_client_timeout: 3 #seconds + {% endif %} +{% else %} + webhook_job_max_retry: 3 + # HTTP client timeout for webhook job + webhook_job_http_client_timeout: 3 #seconds +{% endif %} + +# Log configurations +log: + # options are debug, info, warning, error, fatal +{% if log is defined %} + level: {{ log.level }} + # configs for logs in local storage + local: + # Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated. + rotate_count: {{ log.local.rotate_count }} + # Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes. + # If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G + # are all valid. + rotate_size: {{ log.local.rotate_size }} + # The directory on your host that store log + location: {{ log.local.location }} + {% if log.external_endpoint is defined %} + external_endpoint: + # protocol used to transmit log to external endpoint, options is tcp or udp + protocol: {{ log.external_endpoint.protocol }} + # The host of external endpoint + host: {{ log.external_endpoint.host }} + # Port of external endpoint + port: {{ log.external_endpoint.port }} + {% else %} + # Uncomment following lines to enable external syslog endpoint. + # external_endpoint: + # # protocol used to transmit log to external endpoint, options is tcp or udp + # protocol: tcp + # # The host of external endpoint + # host: localhost + # # Port of external endpoint + # port: 5140 + {% endif %} +{% else %} + level: info + # configs for logs in local storage + local: + # Log files are rotated log_rotate_count times before being removed. If count is 0, old versions are removed rather than rotated. + rotate_count: 50 + # Log files are rotated only if they grow bigger than log_rotate_size bytes. If size is followed by k, the size is assumed to be in kilobytes. + # If the M is used, the size is in megabytes, and if G is used, the size is in gigabytes. So size 100, size 100k, size 100M and size 100G + # are all valid. + rotate_size: 200M + # The directory on your host that store log + location: /var/log/harbor + + # Uncomment following lines to enable external syslog endpoint. + # external_endpoint: + # # protocol used to transmit log to external endpoint, options is tcp or udp + # protocol: tcp + # # The host of external endpoint + # host: localhost + # # Port of external endpoint + # port: 5140 +{% endif %} + + +#This attribute is for migrator to detect the version of the .cfg file, DO NOT MODIFY! +_version: 2.11.0 +{% if external_database is defined %} +# Uncomment external_database if using external database. +external_database: + harbor: + host: {{ external_database.harbor.host }} + port: {{ external_database.harbor.port }} + db_name: {{ external_database.harbor.db_name }} + username: {{ external_database.harbor.username }} + password: {{ external_database.harbor.password }} + ssl_mode: {{ external_database.harbor.ssl_mode }} + max_idle_conns: {{ external_database.harbor.max_idle_conns}} + max_open_conns: {{ external_database.harbor.max_open_conns}} +{% else %} +# Uncomment external_database if using external database. +# external_database: +# harbor: +# host: harbor_db_host +# port: harbor_db_port +# db_name: harbor_db_name +# username: harbor_db_username +# password: harbor_db_password +# ssl_mode: disable +# max_idle_conns: 2 +# max_open_conns: 0 +{% endif %} + +{% if redis is defined %} +redis: +# # db_index 0 is for core, it's unchangeable +{% if redis.registry_db_index is defined %} + registry_db_index: {{ redis.registry_db_index }} +{% else %} +# # registry_db_index: 1 +{% endif %} +{% if redis.jobservice_db_index is defined %} + jobservice_db_index: {{ redis.jobservice_db_index }} +{% else %} +# # jobservice_db_index: 2 +{% endif %} +{% if redis.trivy_db_index is defined %} + trivy_db_index: {{ redis.trivy_db_index }} +{% else %} +# # trivy_db_index: 5 +{% endif %} +{% if redis.harbor_db_index is defined %} + harbor_db_index: {{ redis.harbor_db_index }} +{% else %} +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +{% endif %} +{% if redis.cache_layer_db_index is defined %} + cache_layer_db_index: {{ redis.cache_layer_db_index }} +{% else %} +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 +{% endif %} +{% else %} +# Uncomment redis if need to customize redis db +# redis: +# # db_index 0 is for core, it's unchangeable +# # registry_db_index: 1 +# # jobservice_db_index: 2 +# # trivy_db_index: 5 +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 +{% endif %} + +{% if external_redis is defined %} +external_redis: + # support redis, redis+sentinel + # host for redis: : + # host for redis+sentinel: + # :,:,: + host: {{ external_redis.host }} + password: {{ external_redis.password }} + # Redis AUTH command was extended in Redis 6, it is possible to use it in the two-arguments AUTH form. + {% if external_redis.username is defined %} + username: {{ external_redis.username }} + {% else %} + # username: + {% endif %} + # sentinel_master_set must be set to support redis+sentinel + #sentinel_master_set: + # db_index 0 is for core, it's unchangeable + registry_db_index: {{ external_redis.registry_db_index }} + jobservice_db_index: {{ external_redis.jobservice_db_index }} + trivy_db_index: 5 + idle_timeout_seconds: 30 + {% if external_redis.harbor_db_index is defined %} + harbor_db_index: {{ redis.harbor_db_index }} + {% else %} +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 + {% endif %} + {% if external_redis.cache_layer_db_index is defined %} + cache_layer_db_index: {{ redis.cache_layer_db_index }} + {% else %} +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 + {% endif %} +{% else %} +# Uncomments external_redis if using external Redis server +# external_redis: +# # support redis, redis+sentinel +# # host for redis: : +# # host for redis+sentinel: +# # :,:,: +# host: redis:6379 +# password: +# # Redis AUTH command was extended in Redis 6, it is possible to use it in the two-arguments AUTH form. +# # username: +# # sentinel_master_set must be set to support redis+sentinel +# #sentinel_master_set: +# # db_index 0 is for core, it's unchangeable +# registry_db_index: 1 +# jobservice_db_index: 2 +# trivy_db_index: 5 +# idle_timeout_seconds: 30 +# # it's optional, the db for harbor business misc, by default is 0, uncomment it if you want to change it. +# # harbor_db_index: 6 +# # it's optional, the db for harbor cache layer, by default is 0, uncomment it if you want to change it. +# # cache_layer_db_index: 7 +{% endif %} + +{% if uaa is defined %} +# Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert. +uaa: + ca_file: {{ uaa.ca_file }} +{% else %} +# Uncomment uaa for trusting the certificate of uaa instance that is hosted via self-signed cert. +# uaa: +# ca_file: /path/to/ca +{% endif %} + + +# Global proxy +# Config http proxy for components, e.g. http://my.proxy.com:3128 +# Components doesn't need to connect to each others via http proxy. +# Remove component from `components` array if want disable proxy +# for it. If you want use proxy for replication, MUST enable proxy +# for core and jobservice, and set `http_proxy` and `https_proxy`. +# Add domain to the `no_proxy` field, when you want disable proxy +# for some special registry. +{% if proxy is defined %} +proxy: + http_proxy: {{ proxy.http_proxy or ''}} + https_proxy: {{ proxy.https_proxy or ''}} + no_proxy: {{ proxy.no_proxy or ''}} + {% if proxy.components is defined %} + components: + {% for component in proxy.components %} + {% if component != 'clair' %} + - {{component}} + {% endif %} + {% endfor %} + {% endif %} +{% else %} +proxy: + http_proxy: + https_proxy: + no_proxy: + components: + - core + - jobservice + - trivy +{% endif %} + +{% if metric is defined %} +metric: + enabled: {{ metric.enabled }} + port: {{ metric.port }} + path: {{ metric.path }} +{% else %} +# metric: +# enabled: false +# port: 9090 +# path: /metrics +{% endif %} + +# Trace related config +# only can enable one trace provider(jaeger or otel) at the same time, +# and when using jaeger as provider, can only enable it with agent mode or collector mode. +# if using jaeger collector mode, uncomment endpoint and uncomment username, password if needed +# if using jaeger agetn mode uncomment agent_host and agent_port +{% if trace is defined %} +trace: + enabled: {{ trace.enabled | lower}} + sample_rate: {{ trace.sample_rate }} + # # namespace used to differentiate different harbor services + {% if trace.namespace is defined %} + namespace: {{ trace.namespace }} + {% else %} + # namespace: + {% endif %} + # # attributes is a key value dict contains user defined attributes used to initialize trace provider + {% if trace.attributes is defined%} + attributes: + {% for name, value in trace.attributes.items() %} + {{name}}: {{value}} + {% endfor %} + {% else %} + # attributes: + # application: harbor + {% endif %} + {% if trace.jaeger is defined%} + jaeger: + endpoint: {{trace.jaeger.endpoint or '' }} + username: {{trace.jaeger.username or ''}} + password: {{trace.jaeger.password or ''}} + agent_host: {{trace.jaeger.agent_host or ''}} + agent_port: {{trace.jaeger.agent_port or ''}} + {% else %} + # jaeger: + # endpoint: + # username: + # password: + # agent_host: + # agent_port: + {% endif %} + {% if trace. otel is defined %} + otel: + endpoint: {{trace.otel.endpoint or '' }} + url_path: {{trace.otel.url_path or '' }} + compression: {{trace.otel.compression | lower }} + insecure: {{trace.otel.insecure | lower }} + timeout: {{trace.otel.timeout or '' }} + {% else %} + # otel: + # endpoint: hostname:4318 + # url_path: /v1/traces + # compression: false + # insecure: true + # # timeout is in seconds + # timeout: 10 + {% endif%} +{% else %} +# trace: +# enabled: true +# # set sample_rate to 1 if you wanna sampling 100% of trace data; set 0.5 if you wanna sampling 50% of trace data, and so forth +# sample_rate: 1 +# # # namespace used to differentiate different harbor services +# # namespace: +# # # attributes is a key value dict contains user defined attributes used to initialize trace provider +# # attributes: +# # application: harbor +# # jaeger: +# # endpoint: http://hostname:14268/api/traces +# # username: +# # password: +# # agent_host: hostname +# # agent_port: 6831 +# # otel: +# # endpoint: hostname:4318 +# # url_path: /v1/traces +# # compression: false +# # insecure: true +# # # timeout is in seconds +# # timeout: 10 +{% endif %} + +# enable purge _upload directories +{% if upload_purging is defined %} +upload_purging: + enabled: {{ upload_purging.enabled | lower}} + age: {{ upload_purging.age }} + interval: {{ upload_purging.interval }} + dryrun: {{ upload_purging.dryrun | lower}} +{% else %} +upload_purging: + enabled: true + # remove files in _upload directories which exist for a period of time, default is one week. + age: 168h + # the interval of the purge operations + interval: 24h + dryrun: false +{% endif %} + +# Cache layer related config +{% if cache is defined %} +cache: + enabled: {{ cache.enabled | lower}} + expire_hours: {{ cache.expire_hours }} +{% else %} +cache: + enabled: false + expire_hours: 24 +{% endif %} + +# Harbor core configurations +# Uncomment to enable the following harbor core related configuration items. +{% if core is defined %} +core: + # The provider for updating project quota(usage), there are 2 options, redis or db, + # by default is implemented by db but you can switch the updation via redis which + # can improve the performance of high concurrent pushing to the same project, + # and reduce the database connections spike and occupies. + # By redis will bring up some delay for quota usage updation for display, so only + # suggest switch provider to redis if you were ran into the db connections spike aroud + # the scenario of high concurrent pushing to same project, no improvment for other scenes. + quota_update_provider: {{ core.quota_update_provider }} +{% else %} +# core: +# # The provider for updating project quota(usage), there are 2 options, redis or db, +# # by default is implemented by db but you can switch the updation via redis which +# # can improve the performance of high concurrent pushing to the same project, +# # and reduce the database connections spike and occupies. +# # By redis will bring up some delay for quota usage updation for display, so only +# # suggest switch provider to redis if you were ran into the db connections spike around +# # the scenario of high concurrent pushing to same project, no improvement for other scenes. +# quota_update_provider: redis # Or db +{% endif %} From b7d4bf0d076a64e8dd799f1cab9b4a3eb244c60e Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Mon, 22 Apr 2024 17:43:04 +0800 Subject: [PATCH 037/100] Log and skip adapter ping error when retrieve adapter capability (#20314) Signed-off-by: stonezdj --- src/controller/scanner/base_controller.go | 14 +++++++++----- src/controller/scanner/controller.go | 3 +++ src/server/v2.0/handler/project.go | 12 +++++------- src/server/v2.0/handler/project_test.go | 3 +++ src/testing/controller/scanner/controller.go | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/controller/scanner/base_controller.go b/src/controller/scanner/base_controller.go index 068424a1e..d05878288 100644 --- a/src/controller/scanner/base_controller.go +++ b/src/controller/scanner/base_controller.go @@ -37,6 +37,8 @@ const ( proScannerMetaKey = "projectScanner" statusUnhealthy = "unhealthy" statusHealthy = "healthy" + // RetrieveCapFailMsg the message indicate failed to retrieve the scanner capabilities + RetrieveCapFailMsg = "failed to retrieve scanner capabilities, error %v" ) // DefaultController is a singleton api controller for plug scanners @@ -80,8 +82,9 @@ func (bc *basicController) ListRegistrations(ctx context.Context, query *q.Query return nil, errors.Wrap(err, "api controller: list registrations") } for _, r := range l { - if err := bc.appendCap(ctx, r); err != nil { - return nil, err + if err := bc.RetrieveCap(ctx, r); err != nil { + log.Warningf(RetrieveCapFailMsg, err) + return l, nil } } return l, nil @@ -129,13 +132,14 @@ func (bc *basicController) GetRegistration(ctx context.Context, registrationUUID if r == nil { return nil, nil } - if err := bc.appendCap(ctx, r); err != nil { - return nil, err + if err := bc.RetrieveCap(ctx, r); err != nil { + log.Warningf(RetrieveCapFailMsg, err) + return r, nil } return r, nil } -func (bc *basicController) appendCap(ctx context.Context, r *scanner.Registration) error { +func (bc *basicController) RetrieveCap(ctx context.Context, r *scanner.Registration) error { mt, err := bc.Ping(ctx, r) if err != nil { logger.Errorf("Get registration error: %s", err) diff --git a/src/controller/scanner/controller.go b/src/controller/scanner/controller.go index d2ae007dd..d9558d3e4 100644 --- a/src/controller/scanner/controller.go +++ b/src/controller/scanner/controller.go @@ -154,4 +154,7 @@ type Controller interface { // *v1.ScannerAdapterMetadata : metadata returned by the scanner if successfully ping // error : non nil error if any errors occurred GetMetadata(ctx context.Context, registrationUUID string) (*v1.ScannerAdapterMetadata, error) + + // RetrieveCap retrieve scanner capabilities + RetrieveCap(ctx context.Context, r *scanner.Registration) error } diff --git a/src/server/v2.0/handler/project.go b/src/server/v2.0/handler/project.go index f9848345f..29286f8ec 100644 --- a/src/server/v2.0/handler/project.go +++ b/src/server/v2.0/handler/project.go @@ -590,18 +590,16 @@ func (a *projectAPI) GetScannerOfProject(ctx context.Context, params operation.G return a.SendError(ctx, err) } - scanner, err := a.scannerCtl.GetRegistrationByProject(ctx, p.ProjectID) + s, err := a.scannerCtl.GetRegistrationByProject(ctx, p.ProjectID) if err != nil { return a.SendError(ctx, err) } - if scanner != nil { - metadata, err := a.scannerCtl.GetMetadata(ctx, scanner.UUID) - if err != nil { - return a.SendError(ctx, err) + if s != nil { + if err := a.scannerCtl.RetrieveCap(ctx, s); err != nil { + log.Warningf(scanner.RetrieveCapFailMsg, err) } - scanner.Capabilities = metadata.ConvertCapability() } - return operation.NewGetScannerOfProjectOK().WithPayload(model.NewScannerRegistration(scanner).ToSwagger(ctx)) + return operation.NewGetScannerOfProjectOK().WithPayload(model.NewScannerRegistration(s).ToSwagger(ctx)) } func (a *projectAPI) ListScannerCandidatesOfProject(ctx context.Context, params operation.ListScannerCandidatesOfProjectParams) middleware.Responder { diff --git a/src/server/v2.0/handler/project_test.go b/src/server/v2.0/handler/project_test.go index 9289829b8..eca7f09a3 100644 --- a/src/server/v2.0/handler/project_test.go +++ b/src/server/v2.0/handler/project_test.go @@ -89,6 +89,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(nil, nil).Once() mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() + mock.OnAnything(suite.scannerCtl, "RetrieveCap").Return(nil).Once() res, err := suite.Get("/projects/1/scanner") suite.NoError(err) suite.Equal(200, res.StatusCode) @@ -98,6 +99,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(suite.reg, nil).Once() mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() + mock.OnAnything(suite.scannerCtl, "RetrieveCap").Return(nil).Once() var scanner scanner.Registration res, err := suite.GetJSON("/projects/1/scanner", &scanner) suite.NoError(err) @@ -110,6 +112,7 @@ func (suite *ProjectTestSuite) TestGetScannerOfProject() { mock.OnAnything(suite.projectCtl, "Get").Return(suite.project, nil).Once() mock.OnAnything(suite.scannerCtl, "GetMetadata").Return(suite.metadata, nil).Once() mock.OnAnything(suite.scannerCtl, "GetRegistrationByProject").Return(suite.reg, nil).Once() + mock.OnAnything(suite.scannerCtl, "RetrieveCap").Return(nil).Once() var scanner scanner.Registration res, err := suite.GetJSON("/projects/library/scanner", &scanner) diff --git a/src/testing/controller/scanner/controller.go b/src/testing/controller/scanner/controller.go index 2f7f9777a..38d66f9b9 100644 --- a/src/testing/controller/scanner/controller.go +++ b/src/testing/controller/scanner/controller.go @@ -281,6 +281,24 @@ func (_m *Controller) RegistrationExists(ctx context.Context, registrationUUID s return r0 } +// RetrieveCap provides a mock function with given fields: ctx, r +func (_m *Controller) RetrieveCap(ctx context.Context, r *scanner.Registration) error { + ret := _m.Called(ctx, r) + + if len(ret) == 0 { + panic("no return value specified for RetrieveCap") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, *scanner.Registration) error); ok { + r0 = rf(ctx, r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // SetDefaultRegistration provides a mock function with given fields: ctx, registrationUUID func (_m *Controller) SetDefaultRegistration(ctx context.Context, registrationUUID string) error { ret := _m.Called(ctx, registrationUUID) From c80e9bf4771afd062f5e35440ba2c5cffad82ff6 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Wed, 24 Apr 2024 09:57:46 +0800 Subject: [PATCH 038/100] Add 422 in the swagger.yaml (#20344) change log level with no content message fix time in sbom accessory fixes #20342 #20332 #20328 Signed-off-by: stonezdj --- api/v2.0/swagger.yaml | 16 ++++++++++++++++ src/controller/scan/base_controller.go | 2 +- src/pkg/scan/sbom/sbom.go | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 5f0457790..6890602a6 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -1192,6 +1192,8 @@ paths: $ref: '#/responses/403' '404': $ref: '#/responses/404' + '422': + $ref: '#/responses/422' '500': $ref: '#/responses/500' /projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/stop: @@ -1223,6 +1225,8 @@ paths: $ref: '#/responses/403' '404': $ref: '#/responses/404' + '422': + $ref: '#/responses/422' '500': $ref: '#/responses/500' /projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/scan/{report_id}/log: @@ -1476,6 +1480,8 @@ paths: $ref: '#/responses/403' '404': $ref: '#/responses/404' + '422': + $ref: '#/responses/422' '500': $ref: '#/responses/500' /projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels: @@ -4823,6 +4829,8 @@ paths: $ref: '#/responses/403' '404': $ref: '#/responses/404' + '422': + $ref: '#/responses/422' '500': $ref: '#/responses/500' /schedules: @@ -6456,6 +6464,14 @@ responses: type: string schema: $ref: '#/definitions/Errors' + '422': + description: Unsupported Type + headers: + X-Request-Id: + description: The ID of the corresponding request for the response + type: string + schema: + $ref: '#/definitions/Errors' '500': description: Internal server error headers: diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index c1b68947c..161481711 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -751,7 +751,7 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, reportContent := reports[0].Report result := map[string]interface{}{} if len(reportContent) == 0 { - log.Warning("no content for current report") + log.Debug("no content for current report") return result, nil } err = json.Unmarshal([]byte(reportContent), &result) diff --git a/src/pkg/scan/sbom/sbom.go b/src/pkg/scan/sbom/sbom.go index bbf405571..a3e0f8501 100644 --- a/src/pkg/scan/sbom/sbom.go +++ b/src/pkg/scan/sbom/sbom.go @@ -112,9 +112,11 @@ func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel // annotations defines the annotations for the accessory artifact func (v *scanHandler) annotations() map[string]string { + t := time.Now().Format(time.RFC3339) return map[string]string{ - "created-by": "Harbor", - "org.opencontainers.artifact.created": time.Now().Format(time.RFC3339), + "created": t, + "created-by": "Harbor", + "org.opencontainers.artifact.created": t, "org.opencontainers.artifact.description": "SPDX JSON SBOM", } } From 2af02f3b256de2825887f03d9d82e966019bc315 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Wed, 24 Apr 2024 16:05:14 +0800 Subject: [PATCH 039/100] fix: update image reference to "@" in audit log when pushing & deleting images (#20348) Signed-off-by: Shengwen Yu --- src/controller/event/topic.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/event/topic.go b/src/controller/event/topic.go index d099a8dbb..5898bf4af 100644 --- a/src/controller/event/topic.go +++ b/src/controller/event/topic.go @@ -159,7 +159,7 @@ func (p *PushArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) { ResourceType: "artifact"} if len(p.Tags) == 0 { - auditLog.Resource = fmt.Sprintf("%s:%s", + auditLog.Resource = fmt.Sprintf("%s@%s", p.Artifact.RepositoryName, p.Artifact.Digest) } else { auditLog.Resource = fmt.Sprintf("%s:%s", @@ -222,7 +222,7 @@ func (d *DeleteArtifactEvent) ResolveToAuditLog() (*model.AuditLog, error) { Operation: rbac.ActionDelete.String(), Username: d.Operator, ResourceType: "artifact", - Resource: fmt.Sprintf("%s:%s", d.Artifact.RepositoryName, d.Artifact.Digest)} + Resource: fmt.Sprintf("%s@%s", d.Artifact.RepositoryName, d.Artifact.Digest)} return auditLog, nil } From ec8d692fe6038b2d0f35310ec10fdc665ad0e1c1 Mon Sep 17 00:00:00 2001 From: "stonezdj(Daojun Zhang)" Date: Thu, 25 Apr 2024 17:00:35 +0800 Subject: [PATCH 040/100] Add scanner info and report_id to sbom_overview on listing artifact (#20358) Add scan_status and report_id when scan has a failed task Signed-off-by: stonezdj --- api/v2.0/swagger.yaml | 2 ++ src/controller/scan/base_controller.go | 21 ++++++++++++++++ src/controller/scan/base_controller_test.go | 27 ++++++++++++++++++--- src/pkg/scan/sbom/model/summary.go | 4 +++ src/pkg/scan/sbom/sbom.go | 15 ++++++------ src/server/v2.0/handler/assembler/report.go | 22 ++++++++--------- 6 files changed, 68 insertions(+), 23 deletions(-) diff --git a/api/v2.0/swagger.yaml b/api/v2.0/swagger.yaml index 6890602a6..c9e1e8a50 100644 --- a/api/v2.0/swagger.yaml +++ b/api/v2.0/swagger.yaml @@ -6816,6 +6816,8 @@ definitions: format: int64 description: 'Time in seconds required to create the report' example: 300 + scanner: + $ref: '#/definitions/Scanner' NativeReportSummary: type: object description: 'The summary for the native report' diff --git a/src/controller/scan/base_controller.go b/src/controller/scan/base_controller.go index 161481711..c70221a0f 100644 --- a/src/controller/scan/base_controller.go +++ b/src/controller/scan/base_controller.go @@ -751,6 +751,11 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, reportContent := reports[0].Report result := map[string]interface{}{} if len(reportContent) == 0 { + status := bc.retrieveStatusFromTask(ctx, reports[0].UUID) + if len(status) > 0 { + result[sbomModel.ReportID] = reports[0].UUID + result[sbomModel.ScanStatus] = status + } log.Debug("no content for current report") return result, nil } @@ -758,6 +763,22 @@ func (bc *basicController) GetSBOMSummary(ctx context.Context, art *ar.Artifact, return result, err } +// retrieve the status from task +func (bc *basicController) retrieveStatusFromTask(ctx context.Context, reportID string) string { + if len(reportID) == 0 { + return "" + } + tasks, err := bc.taskMgr.ListScanTasksByReportUUID(ctx, reportID) + if err != nil { + log.Warningf("can not find the task with report UUID %v, error %v", reportID, err) + return "" + } + if len(tasks) > 0 { + return tasks[0].Status + } + return "" +} + // GetScanLog ... func (bc *basicController) GetScanLog(ctx context.Context, artifact *ar.Artifact, uuid string) ([]byte, error) { if len(uuid) == 0 { diff --git a/src/controller/scan/base_controller_test.go b/src/controller/scan/base_controller_test.go index 521325d79..19a559e0e 100644 --- a/src/controller/scan/base_controller_test.go +++ b/src/controller/scan/base_controller_test.go @@ -70,9 +70,10 @@ type ControllerTestSuite struct { tagCtl *tagtesting.FakeController - registration *scanner.Registration - artifact *artifact.Artifact - rawReport string + registration *scanner.Registration + artifact *artifact.Artifact + wrongArtifact *artifact.Artifact + rawReport string execMgr *tasktesting.ExecutionManager taskMgr *tasktesting.Manager @@ -101,6 +102,9 @@ func (suite *ControllerTestSuite) SetupSuite() { suite.artifact.Digest = "digest-code" suite.artifact.ManifestMediaType = v1.MimeTypeDockerArtifact + suite.wrongArtifact = &artifact.Artifact{Artifact: art.Artifact{ID: 2, ProjectID: 1}} + suite.wrongArtifact.Digest = "digest-wrong" + m := &v1.ScannerAdapterMetadata{ Scanner: &v1.Scanner{ Name: "Trivy", @@ -202,8 +206,11 @@ func (suite *ControllerTestSuite) SetupSuite() { Report: `{"sbom_digest": "sha256:1234567890", "scan_status": "Success", "duration": 3, "start_time": "2021-09-01T00:00:00Z", "end_time": "2021-09-01T00:00:03Z"}`, }, } + + emptySBOMReport := []*scan.Report{{Report: ``, UUID: "rp-uuid-004"}} mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeNativeReport}).Return(reports, nil) mgr.On("GetBy", mock.Anything, suite.artifact.Digest, suite.registration.UUID, []string{v1.MimeTypeSBOMReport}).Return(sbomReport, nil) + mgr.On("GetBy", mock.Anything, suite.wrongArtifact.Digest, suite.registration.UUID, []string{v1.MimeTypeSBOMReport}).Return(emptySBOMReport, nil) mgr.On("Get", mock.Anything, "rp-uuid-001").Return(reports[0], nil) mgr.On("UpdateReportData", "rp-uuid-001", suite.rawReport, (int64)(10000)).Return(nil) mgr.On("UpdateStatus", "the-uuid-123", "Success", (int64)(10000)).Return(nil) @@ -654,6 +661,12 @@ func (suite *ControllerTestSuite) TestGenerateSBOMSummary() { suite.NotNil(dgst) suite.Equal("Success", status) suite.Equal("sha256:1234567890", dgst) + tasks := []*task.Task{{Status: "Error"}} + suite.taskMgr.On("ListScanTasksByReportUUID", mock.Anything, "rp-uuid-004").Return(tasks, nil).Once() + sum2, err := suite.c.GetSummary(context.TODO(), suite.wrongArtifact, []string{v1.MimeTypeSBOMReport}) + suite.Nil(err) + suite.NotNil(sum2) + } func TestIsSBOMMimeTypes(t *testing.T) { @@ -683,5 +696,11 @@ func (suite *ControllerTestSuite) TestDeleteArtifactAccessories() { } ctx := orm.NewContext(nil, &ormtesting.FakeOrmer{}) suite.NoError(suite.c.deleteArtifactAccessories(ctx, reports)) - +} + +func (suite *ControllerTestSuite) TestRetrieveStatusFromTask() { + tasks := []*task.Task{{Status: "Error"}} + suite.taskMgr.On("ListScanTasksByReportUUID", mock.Anything, "rp-uuid-004").Return(tasks, nil).Once() + status := suite.c.retrieveStatusFromTask(nil, "rp-uuid-004") + suite.Equal("Error", status) } diff --git a/src/pkg/scan/sbom/model/summary.go b/src/pkg/scan/sbom/model/summary.go index 46c870f97..0d7e6a2ef 100644 --- a/src/pkg/scan/sbom/model/summary.go +++ b/src/pkg/scan/sbom/model/summary.go @@ -27,6 +27,10 @@ const ( Duration = "duration" // ScanStatus ... ScanStatus = "scan_status" + // ReportID ... + ReportID = "report_id" + // Scanner ... + Scanner = "scanner" ) // Summary includes the sbom summary information diff --git a/src/pkg/scan/sbom/sbom.go b/src/pkg/scan/sbom/sbom.go index a3e0f8501..f8e6d2e43 100644 --- a/src/pkg/scan/sbom/sbom.go +++ b/src/pkg/scan/sbom/sbom.go @@ -87,7 +87,7 @@ func (v *scanHandler) RequiredPermissions() []*types.Policy { // PostScan defines task specific operations after the scan is complete func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel.Report, rawReport string, startTime time.Time, robot *model.Robot) (string, error) { - sbomContent, err := retrieveSBOMContent(rawReport) + sbomContent, s, err := retrieveSBOMContent(rawReport) if err != nil { return "", err } @@ -107,7 +107,7 @@ func (v *scanHandler) PostScan(ctx job.Context, sr *v1.ScanRequest, _ *scanModel myLogger.Errorf("error when create accessory from image %v", err) return "", err } - return v.generateReport(startTime, sr.Artifact.Repository, dgst, "Success") + return v.generateReport(startTime, sr.Artifact.Repository, dgst, "Success", s) } // annotations defines the annotations for the accessory artifact @@ -121,7 +121,7 @@ func (v *scanHandler) annotations() map[string]string { } } -func (v *scanHandler) generateReport(startTime time.Time, repository, digest, status string) (string, error) { +func (v *scanHandler) generateReport(startTime time.Time, repository, digest, status string, scanner *v1.Scanner) (string, error) { summary := sbom.Summary{} endTime := time.Now() summary[sbom.StartTime] = startTime @@ -130,6 +130,7 @@ func (v *scanHandler) generateReport(startTime time.Time, repository, digest, st summary[sbom.SBOMRepository] = repository summary[sbom.SBOMDigest] = digest summary[sbom.ScanStatus] = status + summary[sbom.Scanner] = scanner rep, err := json.Marshal(summary) if err != nil { return "", err @@ -150,15 +151,15 @@ func registryFQDN(ctx context.Context) string { } // retrieveSBOMContent retrieves the "sbom" field from the raw report -func retrieveSBOMContent(rawReport string) ([]byte, error) { +func retrieveSBOMContent(rawReport string) ([]byte, *v1.Scanner, error) { rpt := vuln.Report{} err := json.Unmarshal([]byte(rawReport), &rpt) if err != nil { - return nil, err + return nil, nil, err } sbomContent, err := json.Marshal(rpt.SBOM) if err != nil { - return nil, err + return nil, nil, err } - return sbomContent, nil + return sbomContent, rpt.Scanner, nil } diff --git a/src/server/v2.0/handler/assembler/report.go b/src/server/v2.0/handler/assembler/report.go index e4f9657ea..ff11e2b8f 100644 --- a/src/server/v2.0/handler/assembler/report.go +++ b/src/server/v2.0/handler/assembler/report.go @@ -21,16 +21,12 @@ import ( "github.com/goharbor/harbor/src/lib" "github.com/goharbor/harbor/src/lib/log" v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1" + sbomModel "github.com/goharbor/harbor/src/pkg/scan/sbom/model" "github.com/goharbor/harbor/src/server/v2.0/handler/model" ) const ( vulnerabilitiesAddition = "vulnerabilities" - startTime = "start_time" - endTime = "end_time" - scanStatus = "scan_status" - sbomDigest = "sbom_digest" - duration = "duration" ) // NewScanReportAssembler returns vul assembler @@ -92,17 +88,19 @@ func (assembler *ScanReportAssembler) Assemble(ctx context.Context) error { overview, err := assembler.scanCtl.GetSummary(ctx, &artifact.Artifact, []string{v1.MimeTypeSBOMReport}) if err != nil { log.Warningf("get scan summary of artifact %s@%s for %s failed, error:%v", artifact.RepositoryName, artifact.Digest, v1.MimeTypeSBOMReport, err) - } else if len(overview) > 0 { + } + if len(overview) > 0 { artifact.SBOMOverView = map[string]interface{}{ - startTime: overview[startTime], - endTime: overview[endTime], - scanStatus: overview[scanStatus], - sbomDigest: overview[sbomDigest], - duration: overview[duration], + sbomModel.StartTime: overview[sbomModel.StartTime], + sbomModel.EndTime: overview[sbomModel.EndTime], + sbomModel.ScanStatus: overview[sbomModel.ScanStatus], + sbomModel.SBOMDigest: overview[sbomModel.SBOMDigest], + sbomModel.Duration: overview[sbomModel.Duration], + sbomModel.ReportID: overview[sbomModel.ReportID], + sbomModel.Scanner: overview[sbomModel.Scanner], } } } } - return nil } From 0e8dce72be06b5082cc4b867683a1a36bd2ed37e Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Fri, 26 Apr 2024 10:52:11 +0800 Subject: [PATCH 041/100] fix: fresh scanner list when updating scanner (#20366) Signed-off-by: Shengwen Yu --- tests/resources/Harbor-Pages/Vulnerability_Elements.robot | 1 + tests/resources/Util.robot | 1 + tests/robot-cases/Group1-Nightly/Trivy.robot | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/resources/Harbor-Pages/Vulnerability_Elements.robot b/tests/resources/Harbor-Pages/Vulnerability_Elements.robot index e64594a87..593c9d1a7 100644 --- a/tests/resources/Harbor-Pages/Vulnerability_Elements.robot +++ b/tests/resources/Harbor-Pages/Vulnerability_Elements.robot @@ -51,3 +51,4 @@ ${scanner_password_input} //*[@id='scanner-password'] ${scanner_token_input} //*[@id='scanner-token'] ${scanner_apikey_input} //*[@id='scanner-apiKey'] ${scanner_set_default_btn} //*[@id='set-default'] +${scanner_list_refresh_btn} //span[@class='refresh-btn']//clr-icon[@role='none'] diff --git a/tests/resources/Util.robot b/tests/resources/Util.robot index 54972975c..d394a6430 100644 --- a/tests/resources/Util.robot +++ b/tests/resources/Util.robot @@ -76,6 +76,7 @@ Resource Harbor-Pages/Job_Service_Dashboard_Elements.robot Resource Harbor-Pages/SecurityHub.robot Resource Harbor-Pages/SecurityHub_Elements.robot Resource Harbor-Pages/Verify.robot +Resource Harbor-Pages/Vulnerability_Elements.robot Resource Docker-Util.robot Resource CNAB_Util.robot Resource Helm-Util.robot diff --git a/tests/robot-cases/Group1-Nightly/Trivy.robot b/tests/robot-cases/Group1-Nightly/Trivy.robot index 3db9513ed..bcfff07d2 100644 --- a/tests/robot-cases/Group1-Nightly/Trivy.robot +++ b/tests/robot-cases/Group1-Nightly/Trivy.robot @@ -182,7 +182,7 @@ Test Case - External Scanner CRUD Filter Scanner By Name scanner${d} Filter Scanner By Endpoint ${SCANNER_ENDPOINT} Retry Wait Element Count //clr-dg-row 1 - Retry Wait Until Page Contains Element //clr-dg-row[.//span[text()='scanner${d}'] and .//clr-dg-cell[text()='${SCANNER_ENDPOINT}'] and .//span[text()='Healthy'] and .//clr-dg-cell[text()='None']] + Retry Double Keywords When Error Retry Element Click xpath=${scanner_list_refresh_btn} Retry Wait Until Page Contains Element //clr-dg-row[.//span[text()='scanner${d}'] and .//clr-dg-cell[text()='${SCANNER_ENDPOINT}'] and .//span[text()='Healthy'] and .//clr-dg-cell[text()='None']] # Delete this scanner Delete Scanner scanner${d} Close Browser From d0cb200ed5591dbdcfbb794631317bd2460e7839 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Fri, 26 Apr 2024 11:44:00 +0800 Subject: [PATCH 042/100] fix: update nightly test case for verifying audit log of image digest (#20354) Signed-off-by: Shengwen Yu --- tests/robot-cases/Group1-Nightly/Common.robot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/robot-cases/Group1-Nightly/Common.robot b/tests/robot-cases/Group1-Nightly/Common.robot index d6915db20..fdc12f54b 100644 --- a/tests/robot-cases/Group1-Nightly/Common.robot +++ b/tests/robot-cases/Group1-Nightly/Common.robot @@ -885,13 +885,13 @@ Test Case - Audit Log And Purge # pull artifact Docker Pull ${ip}/project${d}/${image}:${tag1} Docker Logout ${ip} - Verify Log ${user} project${d}/${image}:${sha256} artifact pull + Verify Log ${user} project${d}/${image}@${sha256} artifact pull Go Into Repo project${d} ${image} # delete artifact @{tag_list} Create List ${tag1} Multi-delete Artifact @{tag_list} Switch To Logs - Verify Log ${user} project${d}/${image}:${sha256} artifact delete + Verify Log ${user} project${d}/${image}@${sha256} artifact delete Go Into Project project${d} # delete repository Delete Repo project${d} ${image} From 822784aac81fbce27fd320e0008eea87ebf11ae2 Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Fri, 26 Apr 2024 12:28:22 +0800 Subject: [PATCH 043/100] =?UTF-8?q?fix:=20update=20to=20"clr-dg-cell[10]"?= =?UTF-8?q?=20to=20fix=20the=20pull=20time=20tc=20due=20to=20addin?= =?UTF-8?q?=E2=80=A6=20(#20361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: update to "clr-dg-cell[10]" to fix the pull time tc due to adding an SBOM column Signed-off-by: Shengwen Yu --- tests/robot-cases/Group1-Nightly/Common.robot | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/robot-cases/Group1-Nightly/Common.robot b/tests/robot-cases/Group1-Nightly/Common.robot index fdc12f54b..7730491e1 100644 --- a/tests/robot-cases/Group1-Nightly/Common.robot +++ b/tests/robot-cases/Group1-Nightly/Common.robot @@ -1151,8 +1151,8 @@ Test Case - Retain Image Last Pull Time Scan Repo ${tag} Succeed Sleep 15 Reload Page - Retry Wait Element Visible //clr-dg-row//clr-dg-cell[9] - ${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[9] + Retry Wait Element Visible //clr-dg-row//clr-dg-cell[10] + ${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[10] Should Be Empty ${last_pull_time} Switch To Configuration System Setting Set Up Retain Image Last Pull Time disable @@ -1160,8 +1160,8 @@ Test Case - Retain Image Last Pull Time Scan Repo ${tag} Succeed Sleep 15 Reload Page - Retry Wait Element Visible //clr-dg-row//clr-dg-cell[9] - ${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[9] + Retry Wait Element Visible //clr-dg-row//clr-dg-cell[10] + ${last_pull_time}= Get Text //clr-dg-row//clr-dg-cell[10] Should Not Be Empty ${last_pull_time} Close Browser From c791b39a26bf4b5a9c7f88d39025b531ab93f20d Mon Sep 17 00:00:00 2001 From: Shengwen YU Date: Fri, 26 Apr 2024 14:13:00 +0800 Subject: [PATCH 044/100] fix: add stop_scan_payload when call stop scan api (#20353) Signed-off-by: Shengwen Yu --- tests/apitests/python/test_project_permission.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/apitests/python/test_project_permission.py b/tests/apitests/python/test_project_permission.py index 1962c48e3..491eba2dc 100644 --- a/tests/apitests/python/test_project_permission.py +++ b/tests/apitests/python/test_project_permission.py @@ -89,8 +89,11 @@ copy_artifact = Permission("{}/projects/{}/repositories/target_repo/artifacts?fr delete_artifact = Permission("{}/projects/{}/repositories/target_repo/artifacts/{}".format(harbor_base_url, project_name, source_artifact_tag), "DELETE", 200) # 6. Resource scan actions: ['read', 'create', 'stop'] +stop_scan_payload = { + "scan_type": "vulnerability" +} create_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202) -stop_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202) +stop_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, stop_scan_payload) read_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/83be44fd-1234-5678-b49f-4b6d6e8f5730/log".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "get", 404) # 7. Resource tag actions: ['list', 'create', 'delete'] From dee73a44f30b4c60115157e53e3019ff758758b9 Mon Sep 17 00:00:00 2001 From: Lichao Xue <68891670+xuelichao@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:56:23 +0800 Subject: [PATCH 045/100] Fix UI bugs (#20364) Signed-off-by: xuelichao --- .../system-robot-util.ts | 1 + .../artifact-additions.component.html | 11 ++++++- .../artifact-additions.component.spec.ts | 21 ++++++++++++ .../artifact-additions.component.ts | 33 ++++++++++++++----- .../artifact-sbom.component.html | 18 ++++++---- .../artifact-sbom.component.spec.ts | 1 - .../artifact-sbom/artifact-sbom.component.ts | 28 +++++----------- .../artifact-vulnerabilities.component.ts | 30 ++--------------- .../artifact-list-tab.component.html | 4 --- .../artifact-list-tab.component.ts | 2 +- .../project/repository/artifact/artifact.ts | 2 +- .../sbom-tip-histogram.component.ts | 4 +-- .../src/app/shared/units/shared.utils.ts | 11 ++++++- src/portal/src/i18n/lang/de-de-lang.json | 3 +- src/portal/src/i18n/lang/en-us-lang.json | 3 +- src/portal/src/i18n/lang/es-es-lang.json | 3 +- src/portal/src/i18n/lang/fr-fr-lang.json | 3 +- src/portal/src/i18n/lang/ko-kr-lang.json | 3 +- src/portal/src/i18n/lang/pt-br-lang.json | 3 +- src/portal/src/i18n/lang/tr-tr-lang.json | 3 +- src/portal/src/i18n/lang/zh-cn-lang.json | 3 +- src/portal/src/i18n/lang/zh-tw-lang.json | 3 +- 22 files changed, 110 insertions(+), 83 deletions(-) diff --git a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts index f693753ad..6e3b4097c 100644 --- a/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts +++ b/src/portal/src/app/base/left-side-nav/system-robot-accounts/system-robot-util.ts @@ -78,6 +78,7 @@ export const ACTION_RESOURCE_I18N_MAP = { log: 'ROBOT_ACCOUNT.LOG', 'notification-policy': 'ROBOT_ACCOUNT.NOTIFICATION_POLICY', quota: 'ROBOT_ACCOUNT.QUOTA', + sbom: 'ROBOT_ACCOUNT.SBOM', }; export function convertKey(key: string) { diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html index 99208d3f4..1a71ebf3a 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.html @@ -13,10 +13,13 @@ [clrIfActive]="currentTabLinkId === 'vulnerability'"> + @@ -50,6 +55,7 @@ [clrIfActive]="currentTabLinkId === 'build-history'"> @@ -67,6 +73,7 @@ [clrIfActive]="currentTabLinkId === 'summary-link'"> @@ -81,6 +88,7 @@ @@ -97,6 +105,7 @@ diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.spec.ts index 0f8a801e4..c4147cb57 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.spec.ts @@ -4,6 +4,8 @@ import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/additi import { CURRENT_BASE_HREF } from '../../../../../shared/units/utils'; import { SharedTestingModule } from '../../../../../shared/shared.module'; import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ArtifactListPageService } from '../artifact-list-page/artifact-list-page.service'; +import { ClrLoadingState } from '@clr/angular'; describe('ArtifactAdditionsComponent', () => { const mockedAdditionLinks: AdditionLinks = { @@ -12,6 +14,18 @@ describe('ArtifactAdditionsComponent', () => { href: CURRENT_BASE_HREF + '/test', }, }; + const mockedArtifactListPageService = { + hasScannerSupportSBOM(): boolean { + return true; + }, + hasEnabledScanner(): boolean { + return true; + }, + getScanBtnState(): ClrLoadingState { + return ClrLoadingState.SUCCESS; + }, + init() {}, + }; let component: ArtifactAdditionsComponent; let fixture: ComponentFixture; @@ -20,6 +34,12 @@ describe('ArtifactAdditionsComponent', () => { imports: [SharedTestingModule], declarations: [ArtifactAdditionsComponent], schemas: [NO_ERRORS_SCHEMA], + providers: [ + { + provide: ArtifactListPageService, + useValue: mockedArtifactListPageService, + }, + ], }).compileComponents(); }); @@ -27,6 +47,7 @@ describe('ArtifactAdditionsComponent', () => { fixture = TestBed.createComponent(ArtifactAdditionsComponent); component = fixture.componentInstance; component.additionLinks = mockedAdditionLinks; + component.tab = 'vulnerability'; fixture.detectChanges(); }); diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts index 45994ac8e..a0f5007b8 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-additions.component.ts @@ -10,7 +10,8 @@ import { ADDITIONS } from './models'; import { AdditionLinks } from '../../../../../../../ng-swagger-gen/models/addition-links'; import { AdditionLink } from '../../../../../../../ng-swagger-gen/models/addition-link'; import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact'; -import { ClrTabs } from '@clr/angular'; +import { ClrLoadingState, ClrTabs } from '@clr/angular'; +import { ArtifactListPageService } from '../artifact-list-page/artifact-list-page.service'; @Component({ selector: 'artifact-additions', @@ -32,14 +33,21 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit { @Input() tab: string; - @Input() currentTabLinkId: string = 'vulnerability'; + @Input() currentTabLinkId: string = ''; activeTab: string = null; @ViewChild('additionsTab') tabs: ClrTabs; - constructor(private ref: ChangeDetectorRef) {} + constructor( + private ref: ChangeDetectorRef, + private artifactListPageService: ArtifactListPageService + ) {} ngOnInit(): void { this.activeTab = this.tab; + if (!this.activeTab) { + this.currentTabLinkId = 'vulnerability'; + } + this.artifactListPageService.init(this.projectId); } ngAfterViewChecked() { @@ -50,6 +58,10 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit { this.ref.detectChanges(); } + hasScannerSupportSBOM(): boolean { + return this.artifactListPageService.hasScannerSupportSBOM(); + } + getVulnerability(): AdditionLink { if ( this.additionLinks && @@ -59,12 +71,7 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit { } return null; } - getSbom(): AdditionLink { - if (this.additionLinks && this.additionLinks[ADDITIONS.SBOMS]) { - return this.additionLinks[ADDITIONS.SBOMS]; - } - return {}; - } + getBuildHistory(): AdditionLink { if (this.additionLinks && this.additionLinks[ADDITIONS.BUILD_HISTORY]) { return this.additionLinks[ADDITIONS.BUILD_HISTORY]; @@ -93,4 +100,12 @@ export class ArtifactAdditionsComponent implements AfterViewChecked, OnInit { actionTab(tab: string): void { this.currentTabLinkId = tab; } + + getScanBtnState(): ClrLoadingState { + return this.artifactListPageService.getScanBtnState(); + } + + hasEnabledScanner(): boolean { + return this.artifactListPageService.hasEnabledScanner(); + } } diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html index c7b9cf8a6..577711f33 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.html @@ -32,12 +32,18 @@
- {{ - 'SBOM.GRID.COLUMN_PACKAGE' | translate - }} - {{ - 'SBOM.GRID.COLUMN_VERSION' | translate - }} + {{ 'SBOM.GRID.COLUMN_PACKAGE' | translate }} + {{ 'SBOM.GRID.COLUMN_VERSION' | translate }} {{ 'SBOM.GRID.COLUMN_LICENSE' | translate }} diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts index e3978ad39..09e68430a 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.spec.ts @@ -10,7 +10,6 @@ import { } from '@ngx-translate/core'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { UserPermissionService } from '../../../../../../shared/services'; -import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link'; import { ErrorHandler } from '../../../../../../shared/units/error-handler'; import { SessionService } from '../../../../../../shared/services/session.service'; import { SessionUser } from '../../../../../../shared/entities/session-user'; diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts index ac352ff0f..c37ee3c16 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-sbom/artifact-sbom.component.ts @@ -1,13 +1,6 @@ -import { - AfterViewInit, - Component, - Input, - OnDestroy, - OnInit, -} from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular'; import { finalize } from 'rxjs/operators'; -import { AdditionLink } from '../../../../../../../../ng-swagger-gen/models/addition-link'; import { ScannerVo, UserPermissionService, @@ -30,7 +23,6 @@ import { HarborEvent, } from '../../../../../../services/event-service/event.service'; import { severityText } from '../../../../../left-side-nav/interrogation-services/vulnerability-database/security-hub.interface'; -import { AppConfigService } from 'src/app/services/app-config.service'; import { ArtifactSbom, @@ -38,8 +30,7 @@ import { getArtifactSbom, } from '../../artifact'; import { ArtifactService } from 'ng-swagger-gen/services'; -import { ScanTypes } from 'src/app/shared/entities/shared.const'; -import { ArtifactListPageService } from '../../artifact-list-page/artifact-list-page.service'; +import { ScanTypes } from '../../../../../../shared/entities/shared.const'; @Component({ selector: 'hbr-artifact-sbom', @@ -56,13 +47,12 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy { @Input() sbomDigest: string; @Input() artifact: Artifact; + @Input() hasScannerSupportSBOM: boolean = false; artifactSbom: ArtifactSbom; loading: boolean = false; - hasScannerSupportSBOM: boolean = false; downloadSbomBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; hasSbomPermission: boolean = false; - hasShowLoading: boolean = false; sub: Subscription; hasViewInitWithDelay: boolean = false; @@ -73,16 +63,13 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy { readonly severityText = severityText; constructor( private errorHandler: ErrorHandler, - private appConfigService: AppConfigService, private artifactService: ArtifactService, - private artifactListPageService: ArtifactListPageService, private userPermissionService: UserPermissionService, private eventService: EventService, private session: SessionService ) {} ngOnInit() { - this.artifactListPageService.init(this.projectId); this.getSbom(); this.getSbomPermission(); if (!this.sub) { @@ -222,8 +209,6 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy { } canDownloadSbom(): boolean { - this.hasScannerSupportSBOM = - this.artifactListPageService.hasScannerSupportSBOM(); return ( this.hasScannerSupportSBOM && //this.hasSbomPermission && @@ -234,7 +219,12 @@ export class ArtifactSbomComponent implements OnInit, OnDestroy { } artifactSbomPackages(): ArtifactSbomPackageItem[] { - return this.artifactSbom?.sbomPackage?.packages ?? []; + return ( + this.artifactSbom?.sbomPackage?.packages?.filter( + item => + item?.name || item?.versionInfo || item?.licenseConcluded + ) ?? [] + ); } load(state: ClrDatagridStateInterface) { diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component.ts b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component.ts index 02ad708ea..9d83d167c 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component.ts +++ b/src/portal/src/app/base/project/repository/artifact/artifact-additions/artifact-vulnerabilities/artifact-vulnerabilities.component.ts @@ -50,14 +50,13 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy { @Input() digest: string; @Input() artifact: Artifact; + @Input() scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; + @Input() hasEnabledScanner: boolean = false; scan_overview: any; scanner: ScannerVo; - projectScanner: ScannerVo; scanningResults: VulnerabilityItem[] = []; loading: boolean = false; - hasEnabledScanner: boolean = false; - scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; severitySort: ClrDatagridComparatorInterface; cvssSort: ClrDatagridComparatorInterface; hasScanningPermission: boolean = false; @@ -112,7 +111,6 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy { ngOnInit() { this.getVulnerabilities(); this.getScanningPermission(); - this.getProjectScanner(); if (!this.sub) { this.sub = this.eventService.subscribe( HarborEvent.UPDATE_VULNERABILITY_INFO, @@ -203,30 +201,6 @@ export class ArtifactVulnerabilitiesComponent implements OnInit, OnDestroy { ); } - getProjectScanner(): void { - this.hasEnabledScanner = false; - this.scanBtnState = ClrLoadingState.LOADING; - this.scanningService.getProjectScanner(this.projectId).subscribe( - response => { - if ( - response && - '{}' !== JSON.stringify(response) && - !response.disabled && - response.health === 'healthy' - ) { - this.scanBtnState = ClrLoadingState.SUCCESS; - this.hasEnabledScanner = true; - } else { - this.scanBtnState = ClrLoadingState.ERROR; - } - this.projectScanner = response; - }, - error => { - this.scanBtnState = ClrLoadingState.ERROR; - } - ); - } - getLevel(v: VulnerabilityItem): number { if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) { return SEVERITY_LEVEL_MAP[v.severity]; diff --git a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html index d523b67c1..f8d10c908 100644 --- a/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html +++ b/src/portal/src/app/base/project/repository/artifact/artifact-list-page/artifact-list/artifact-list-tab/artifact-list-tab.component.html @@ -65,10 +65,6 @@ class="action-dropdown" clrPosition="bottom-left" *clrIfOpen> -