Merge branch 'main' into dependabot/npm_and_yarn/src/portal/express-4.19.2

This commit is contained in:
Shengwen YU 2024-03-26 14:45:11 +08:00 committed by GitHub
commit 40aae673a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 157 additions and 34 deletions

View File

@ -154,11 +154,8 @@ func (c *controller) Start(ctx context.Context, policy *replicationmodel.Policy,
func (c *controller) markError(ctx context.Context, executionID int64, err error) {
logger := log.GetLogger(ctx)
// try to stop the execution first in case that some tasks are already created
if err := c.execMgr.StopAndWait(ctx, executionID, 10*time.Second); err != nil {
logger.Errorf("failed to stop the execution %d: %v", executionID, err)
}
if err := c.execMgr.MarkError(ctx, executionID, err.Error()); err != nil {
logger.Errorf("failed to mark error for the execution %d: %v", executionID, err)
if e := c.execMgr.StopAndWaitWithError(ctx, executionID, 10*time.Second, err); e != nil {
logger.Errorf("failed to stop the execution %d: %v", executionID, e)
}
}

View File

@ -75,8 +75,7 @@ func (r *replicationTestSuite) TestStart() {
// got error when running the replication flow
r.execMgr.On("Create", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
r.execMgr.On("Get", mock.Anything, mock.Anything).Return(&task.Execution{}, nil)
r.execMgr.On("StopAndWait", mock.Anything, mock.Anything, mock.Anything).Return(nil)
r.execMgr.On("MarkError", mock.Anything, mock.Anything, mock.Anything).Return(nil)
r.execMgr.On("StopAndWaitWithError", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
r.flowCtl.On("Start", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("error"))
r.ormCreator.On("Create").Return(nil)
id, err = r.ctl.Start(context.Background(), &repctlmodel.Policy{Enabled: true}, nil, task.ExecutionTriggerManual)

View File

@ -280,12 +280,8 @@ func (r *defaultController) TriggerRetentionExec(ctx context.Context, policyID i
if num, err := r.launcher.Launch(ctx, p, id, dryRun); err != nil {
logger.Errorf("failed to launch the retention jobs, err: %v", err)
if err = r.execMgr.StopAndWait(ctx, id, 10*time.Second); err != nil {
logger.Errorf("failed to stop the retention execution %d: %v", id, err)
}
if err = r.execMgr.MarkError(ctx, id, err.Error()); err != nil {
logger.Errorf("failed to mark error for the retention execution %d: %v", id, err)
if e := r.execMgr.StopAndWaitWithError(ctx, id, 10*time.Second, err); e != nil {
logger.Errorf("failed to stop the retention execution %d: %v", id, e)
}
} else if num == 0 {
// no candidates, mark the execution as done directly

View File

@ -119,11 +119,8 @@ func (c *controller) createCleanupTask(ctx context.Context, jobParams job.Parame
func (c *controller) markError(ctx context.Context, executionID int64, err error) {
// try to stop the execution first in case that some tasks are already created
if err := c.execMgr.StopAndWait(ctx, executionID, 10*time.Second); err != nil {
log.Errorf("failed to stop the execution %d: %v", executionID, err)
}
if err := c.execMgr.MarkError(ctx, executionID, err.Error()); err != nil {
log.Errorf("failed to mark error for the execution %d: %v", executionID, err)
if e := c.execMgr.StopAndWaitWithError(ctx, executionID, 10*time.Second, err); e != nil {
log.Errorf("failed to stop the execution %d: %v", executionID, e)
}
}

View File

@ -117,7 +117,7 @@ func (suite *SystemArtifactCleanupTestSuite) TestStartCleanupErrorDuringTaskCrea
suite.taskMgr.On("Create", ctx, executionID, mock.Anything).Return(taskId, errors.New("test error")).Once()
suite.execMgr.On("MarkError", ctx, executionID, mock.Anything).Return(nil).Once()
suite.execMgr.On("StopAndWait", ctx, executionID, mock.Anything).Return(nil).Once()
suite.execMgr.On("StopAndWaitWithError", ctx, executionID, mock.Anything, mock.Anything).Return(nil).Once()
err := suite.ctl.Start(ctx, false, "SCHEDULE")
suite.Error(err)

View File

@ -56,7 +56,7 @@ func (m *manager) Create(ctx context.Context, jobLog *models.JobLog) (id int64,
return m.dao.Create(ctx, jobLog)
}
// DeleteJobLogsBefore ...
// DeleteBefore ...
func (m *manager) DeleteBefore(ctx context.Context, t time.Time) (id int64, err error) {
return m.dao.DeleteBefore(ctx, t)
}

View File

@ -25,6 +25,8 @@ import (
"github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm"
"github.com/goharbor/harbor/src/lib/q"
"github.com/google/uuid"
)
// TaskDAO is the data access object interface for task
@ -91,22 +93,33 @@ func (t *taskDAO) List(ctx context.Context, query *q.Query) ([]*Task, error) {
return tasks, nil
}
func isValidUUID(id string) bool {
if len(id) == 0 {
return false
}
if _, err := uuid.Parse(id); err != nil {
return false
}
return true
}
func (t *taskDAO) ListScanTasksByReportUUID(ctx context.Context, uuid string) ([]*Task, error) {
ormer, err := orm.FromContext(ctx)
if err != nil {
return nil, err
}
tasks := []*Task{}
// Due to the limitation of the beego's orm, the SQL cannot be converted by orm framework,
// so we can only execute the query by raw SQL, the SQL filters the task contains the report uuid in the column extra_attrs,
// consider from performance side which can using indexes to speed up queries.
sql := fmt.Sprintf(`SELECT * FROM task WHERE extra_attrs::jsonb->'report_uuids' @> '["%s"]'`, uuid)
_, err = ormer.Raw(sql).QueryRows(&tasks)
if !isValidUUID(uuid) {
return nil, errors.BadRequestError(fmt.Errorf("invalid UUID %v", uuid))
}
var tasks []*Task
param := fmt.Sprintf(`{"report_uuids":["%s"]}`, uuid)
sql := `SELECT * FROM task WHERE extra_attrs::jsonb @> cast( ? as jsonb )`
_, err = ormer.Raw(sql, param).QueryRows(&tasks)
if err != nil {
return nil, err
}
return tasks, nil
}

View File

@ -113,8 +113,9 @@ func (t *taskDAOTestSuite) TestList() {
}
func (t *taskDAOTestSuite) TestListScanTasksByReportUUID() {
reportUUID := `7f20b1b9-6117-4a2e-820b-e4cc0401f15e`
// should not exist if non set
tasks, err := t.taskDAO.ListScanTasksByReportUUID(t.ctx, "fake-report-uuid")
tasks, err := t.taskDAO.ListScanTasksByReportUUID(t.ctx, reportUUID)
t.Require().Nil(err)
t.Require().Len(tasks, 0)
// create one with report uuid
@ -122,12 +123,12 @@ func (t *taskDAOTestSuite) TestListScanTasksByReportUUID() {
ExecutionID: t.executionID,
Status: "success",
StatusCode: 1,
ExtraAttrs: `{"report_uuids": ["fake-report-uuid"]}`,
ExtraAttrs: fmt.Sprintf(`{"report_uuids": ["%s"]}`, reportUUID),
})
t.Require().Nil(err)
defer t.taskDAO.Delete(t.ctx, taskID)
// should exist as created
tasks, err = t.taskDAO.ListScanTasksByReportUUID(t.ctx, "fake-report-uuid")
tasks, err = t.taskDAO.ListScanTasksByReportUUID(t.ctx, reportUUID)
t.Require().Nil(err)
t.Require().Len(tasks, 1)
t.Equal(taskID, tasks[0].ID)
@ -299,6 +300,29 @@ func (t *taskDAOTestSuite) TestExecutionIDsByVendorAndStatus() {
defer t.taskDAO.Delete(t.ctx, tid)
}
func TestIsValidUUID(t *testing.T) {
tests := []struct {
name string
uuid string
expected bool
}{
{"Valid UUID", "7f20b1b9-6117-4a2e-820b-e4cc0401f15f", true},
{"Invalid UUID - Short", "7f20b1b9-6117-4a2e-820b", false},
{"Invalid UUID - Long", "7f20b1b9-6117-4a2e-820b-e4cc0401f15f-extra", false},
{"Invalid UUID - Invalid Characters", "7f20b1b9-6117-4z2e-820b-e4cc0401f15f", false},
{"Empty String", "", false},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := isValidUUID(test.uuid)
if result != test.expected {
t.Errorf("Expected isValidUUID(%s) to be %t, got %t", test.uuid, test.expected, result)
}
})
}
}
func TestTaskDAOSuite(t *testing.T) {
suite.Run(t, &taskDAOTestSuite{})
}

View File

@ -59,6 +59,8 @@ type ExecutionManager interface {
// StopAndWait stops all linked tasks of the specified execution and waits until all tasks are stopped
// or get an error
StopAndWait(ctx context.Context, id int64, timeout time.Duration) (err error)
// StopAndWaitWithError calls the StopAndWait first, if it doesn't return error, then it call MarkError if the origError is not empty
StopAndWaitWithError(ctx context.Context, id int64, timeout time.Duration, origError error) (err error)
// Delete the specified execution and its tasks
Delete(ctx context.Context, id int64) (err error)
// Delete all executions and tasks of the specific vendor. They can be deleted only when all the executions/tasks
@ -250,6 +252,16 @@ func (e *executionManager) StopAndWait(ctx context.Context, id int64, timeout ti
}
}
func (e *executionManager) StopAndWaitWithError(ctx context.Context, id int64, timeout time.Duration, origError error) error {
if err := e.StopAndWait(ctx, id, timeout); err != nil {
return err
}
if origError != nil {
return e.MarkError(ctx, id, origError.Error())
}
return nil
}
func (e *executionManager) Delete(ctx context.Context, id int64) error {
tasks, err := e.taskDAO.List(ctx, &q.Query{
Keywords: map[string]interface{}{

View File

@ -139,9 +139,21 @@
<clr-dg-column class="width-240" [clrDgField]="'url'">{{
'SCANNER.ENDPOINT' | translate
}}</clr-dg-column>
<clr-dg-column>{{ 'SCANNER.HEALTH' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'SCANNER.ENABLED' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'SCANNER.AUTH' | translate }}</clr-dg-column>
<clr-dg-column class="width-120">{{
'SCANNER.HEALTH' | translate
}}</clr-dg-column>
<clr-dg-column class="width-120">{{
'SCANNER.ENABLED' | translate
}}</clr-dg-column>
<clr-dg-column class="width-120">{{
'SCANNER.AUTH' | translate
}}</clr-dg-column>
<clr-dg-column class="width-120">{{
'SCANNER.VULNERABILITY' | translate
}}</clr-dg-column>
<clr-dg-column class="width-120">{{
'SCANNER.SBOM' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'SCANNER.DESCRIPTION' | translate
}}</clr-dg-column>
@ -198,6 +210,18 @@
<clr-dg-cell>{{
scanner.auth ? scanner.auth : 'None'
}}</clr-dg-cell>
<clr-dg-cell>{{
(supportCapability(scanner, 'vulnerability')
? 'SCANNER.SUPPORTED'
: 'SCANNER.NOT_SUPPORTED'
) | translate
}}</clr-dg-cell>
<clr-dg-cell>{{
(supportCapability(scanner, 'sbom')
? 'SCANNER.SUPPORTED'
: 'SCANNER.NOT_SUPPORTED'
) | translate
}}</clr-dg-cell>
<clr-dg-cell>{{ scanner.description }}</clr-dg-cell>
<scanner-metadata
*clrIfExpanded

View File

@ -18,6 +18,10 @@
min-width: 240px !important;
}
.width-120 {
min-width: 120px !important;
}
.margin-top-0 {
margin-top: 0;
}

View File

@ -178,6 +178,17 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
addSuccess() {
this.getScanners();
}
supportCapability(scanner: Scanner, capabilityType: string): boolean {
return scanner && scanner.metadata && capabilityType
? (
scanner?.metadata?.capabilities?.filter(
({ type }) => type === capabilityType
) ?? []
).length >= 1
: false;
}
changeStat() {
if (this.selectedRow) {
let scanner: ScannerRegistrationReq = clone(this.selectedRow);

View File

@ -1433,6 +1433,10 @@
"NAME_REQUIRED": "Name ist erforderlich",
"NAME_REX": "Der Name muss mindestens 2 Zeichen lang sein. Mit Kleinbuchstaben, Ziffern und ._-. Er muss mit Buchstaben oder Ziffern beginnen.",
"DESCRIPTION": "Beschreibung",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "Endpunkt",
"ENDPOINT_EXISTS": "URL existiert bereits",
"ENDPOINT_REQUIRED": "URL ist erforderlich",

View File

@ -1434,6 +1434,10 @@
"NAME_REQUIRED": "Name is required",
"NAME_REX": "Name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"DESCRIPTION": "Description",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "Endpoint",
"ENDPOINT_EXISTS": "EndpointUrl already exists",
"ENDPOINT_REQUIRED": "EndpointUrl is required",

View File

@ -1430,6 +1430,10 @@
"NAME_REQUIRED": "Name is required",
"NAME_REX": "Name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"DESCRIPTION": "Description",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "Endpoint",
"ENDPOINT_EXISTS": "EndpointUrl already exists",
"ENDPOINT_REQUIRED": "EndpointUrl is required",

View File

@ -1431,6 +1431,10 @@
"NAME_REQUIRED": "Le nom est requis",
"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",
"ENDPOINT": "Endpoint",
"ENDPOINT_EXISTS": "L'URL de l'endpoint existe déjà",
"ENDPOINT_REQUIRED": "L'URL de l'endpoint est requise",

View File

@ -1430,6 +1430,10 @@
"NAME_REQUIRED": "Nome é obrigatório",
"NAME_REX": "O nome precisa ter no mínimo 2 caracteres. Pode conter apenas letras minúsculas, numeros, ou símbolos ._- e deve começar por uma letra ou número.",
"DESCRIPTION": "Descrição",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "Endereço",
"ENDPOINT_EXISTS": "Endereço já usado por outro examinador",
"ENDPOINT_REQUIRED": "Endereço é obrigatório",

View File

@ -1433,6 +1433,10 @@
"NAME_REQUIRED": "Name is required",
"NAME_REX": "Name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"DESCRIPTION": "Description",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "Endpoint",
"ENDPOINT_EXISTS": "EndpointUrl already exists",
"ENDPOINT_REQUIRED": "EndpointUrl is required",

View File

@ -1429,6 +1429,10 @@
"NAME_REQUIRED": "名称为必填项",
"NAME_REX": "名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"DESCRIPTION": "描述",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "地址",
"ENDPOINT_EXISTS": "地址已存在",
"ENDPOINT_REQUIRED": "地址为必填项",

View File

@ -1428,6 +1428,10 @@
"NAME_REQUIRED": "名稱必填",
"NAME_REX": "名稱必須由小寫字母、數字和 ._- 組成至少2個字元並且必須以字母或數字開頭。",
"DESCRIPTION": "描述",
"SBOM": "SBOM",
"VULNERABILITY": "Vulnerability",
"SUPPORTED": "Supported",
"NOT_SUPPORTED": "Not Supported",
"ENDPOINT": "端點",
"ENDPOINT_EXISTS": "端點 URL 已存在",
"ENDPOINT_REQUIRED": "端點 URL 必填",

View File

@ -57,7 +57,7 @@ func (n *WebhookPolicy) ToTargets() []*models.WebhookTargetObject {
return results
}
// NewNotifiactionPolicy ...
// NewWebhookPolicy ...
func NewWebhookPolicy(p *model.Policy) *WebhookPolicy {
return &WebhookPolicy{
Policy: p,

View File

@ -209,6 +209,20 @@ func (_m *ExecutionManager) StopAndWait(ctx context.Context, id int64, timeout t
return r0
}
// StopAndWaitWithError provides a mock function with given fields: ctx, id, timeout, origError
func (_m *ExecutionManager) StopAndWaitWithError(ctx context.Context, id int64, timeout time.Duration, origError error) error {
ret := _m.Called(ctx, id, timeout, origError)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, int64, time.Duration, error) error); ok {
r0 = rf(ctx, id, timeout, origError)
} else {
r0 = ret.Error(0)
}
return r0
}
// UpdateExtraAttrs provides a mock function with given fields: ctx, id, extraAttrs
func (_m *ExecutionManager) UpdateExtraAttrs(ctx context.Context, id int64, extraAttrs map[string]interface{}) error {
ret := _m.Called(ctx, id, extraAttrs)