mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-25 03:35:21 +01:00
Merge branch 'main' into fix/invalid-repo-artifacts-404
This commit is contained in:
commit
2651e236d8
3
Makefile
3
Makefile
@ -165,7 +165,6 @@ GOIMAGEBUILD_CORE=$(GOIMAGEBUILDCMD) $(GOFLAGS) ${GOTAGS} --ldflags "-w -s $(COR
|
||||
GOBUILDPATH_CORE=$(GOBUILDPATHINCONTAINER)/src/core
|
||||
GOBUILDPATH_JOBSERVICE=$(GOBUILDPATHINCONTAINER)/src/jobservice
|
||||
GOBUILDPATH_REGISTRYCTL=$(GOBUILDPATHINCONTAINER)/src/registryctl
|
||||
GOBUILDPATH_MIGRATEPATCH=$(GOBUILDPATHINCONTAINER)/src/cmd/migrate-patch
|
||||
GOBUILDPATH_STANDALONE_DB_MIGRATOR=$(GOBUILDPATHINCONTAINER)/src/cmd/standalone-db-migrator
|
||||
GOBUILDPATH_EXPORTER=$(GOBUILDPATHINCONTAINER)/src/cmd/exporter
|
||||
GOBUILDMAKEPATH=make
|
||||
@ -182,7 +181,6 @@ JOBSERVICEBINARYPATH=$(BUILDPATH)/$(GOBUILDMAKEPATH_JOBSERVICE)
|
||||
JOBSERVICEBINARYNAME=harbor_jobservice
|
||||
REGISTRYCTLBINARYPATH=$(BUILDPATH)/$(GOBUILDMAKEPATH_REGISTRYCTL)
|
||||
REGISTRYCTLBINARYNAME=harbor_registryctl
|
||||
MIGRATEPATCHBINARYNAME=migrate-patch
|
||||
STANDALONE_DB_MIGRATOR_BINARYPATH=$(BUILDPATH)/$(GOBUILDMAKEPATH_STANDALONE_DB_MIGRATOR)
|
||||
STANDALONE_DB_MIGRATOR_BINARYNAME=migrate
|
||||
|
||||
@ -548,7 +546,6 @@ cleanbinary:
|
||||
if [ -f $(CORE_BINARYPATH)/$(CORE_BINARYNAME) ] ; then rm $(CORE_BINARYPATH)/$(CORE_BINARYNAME) ; fi
|
||||
if [ -f $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ] ; then rm $(JOBSERVICEBINARYPATH)/$(JOBSERVICEBINARYNAME) ; fi
|
||||
if [ -f $(REGISTRYCTLBINARYPATH)/$(REGISTRYCTLBINARYNAME) ] ; then rm $(REGISTRYCTLBINARYPATH)/$(REGISTRYCTLBINARYNAME) ; fi
|
||||
if [ -f $(MIGRATEPATCHBINARYPATH)/$(MIGRATEPATCHBINARYNAME) ] ; then rm $(MIGRATEPATCHBINARYPATH)/$(MIGRATEPATCHBINARYNAME) ; fi
|
||||
rm -rf make/photon/*/binary/
|
||||
|
||||
cleanbaseimage:
|
||||
|
@ -109,7 +109,7 @@ paths:
|
||||
operationId: searchLdapUser
|
||||
summary: Search available ldap users.
|
||||
description: |
|
||||
This endpoint searches the available ldap users based on related configuration parameters. Support searched by input ladp configuration, load configuration from the system and specific filter.
|
||||
This endpoint searches the available ldap users based on related configuration parameters. Support searched by input ldap configuration, load configuration from the system and specific filter.
|
||||
parameters:
|
||||
- $ref: '#/parameters/requestId'
|
||||
- name: username
|
||||
@ -1548,6 +1548,88 @@ paths:
|
||||
$ref: '#/responses/409'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
/projects/{project_name_or_id}/artifacts:
|
||||
get:
|
||||
summary: List artifacts
|
||||
description: List artifacts of the specified project
|
||||
tags:
|
||||
- project
|
||||
operationId: listArtifactsOfProject
|
||||
parameters:
|
||||
- $ref: '#/parameters/requestId'
|
||||
- $ref: '#/parameters/isResourceName'
|
||||
- $ref: '#/parameters/projectNameOrId'
|
||||
- $ref: '#/parameters/query'
|
||||
- $ref: '#/parameters/sort'
|
||||
- $ref: '#/parameters/page'
|
||||
- $ref: '#/parameters/pageSize'
|
||||
- $ref: '#/parameters/acceptVulnerabilities'
|
||||
- name: with_tag
|
||||
in: query
|
||||
description: Specify whether the tags are included inside the returning artifacts
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
- name: with_label
|
||||
in: query
|
||||
description: Specify whether the labels are included inside the returning artifacts
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
- name: with_scan_overview
|
||||
in: query
|
||||
description: Specify whether the scan overview is included inside the returning artifacts
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
- name: with_sbom_overview
|
||||
in: query
|
||||
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
|
||||
- name: with_immutable_status
|
||||
in: query
|
||||
description: Specify whether the immutable status is included inside the tags of the returning artifacts. Only works when setting "with_immutable_status=true"
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
- name: with_accessory
|
||||
in: query
|
||||
description: Specify whether the accessories are included of the returning artifacts. Only works when setting "with_accessory=true"
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
- name: latest_in_repository
|
||||
in: query
|
||||
description: Specify whether only the latest pushed artifact of each repository is included inside the returning artifacts. Only works when either artifact_type or media_type is included in the query.
|
||||
type: boolean
|
||||
required: false
|
||||
default: false
|
||||
responses:
|
||||
'200':
|
||||
description: Success
|
||||
headers:
|
||||
X-Total-Count:
|
||||
description: The total count of artifacts
|
||||
type: integer
|
||||
Link:
|
||||
description: Link refers to the previous page and next page
|
||||
type: string
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/Artifact'
|
||||
'400':
|
||||
$ref: '#/responses/400'
|
||||
'401':
|
||||
$ref: '#/responses/401'
|
||||
'403':
|
||||
$ref: '#/responses/403'
|
||||
'404':
|
||||
$ref: '#/responses/404'
|
||||
'500':
|
||||
$ref: '#/responses/500'
|
||||
'/projects/{project_name_or_id}/scanner':
|
||||
get:
|
||||
summary: Get project level scanner
|
||||
@ -6586,6 +6668,9 @@ definitions:
|
||||
manifest_media_type:
|
||||
type: string
|
||||
description: The manifest media type of the artifact
|
||||
artifact_type:
|
||||
type: string
|
||||
description: The artifact_type in the manifest of the artifact
|
||||
project_id:
|
||||
type: integer
|
||||
format: int64
|
||||
@ -6594,6 +6679,9 @@ definitions:
|
||||
type: integer
|
||||
format: int64
|
||||
description: The ID of the repository that the artifact belongs to
|
||||
repository_name:
|
||||
type: string
|
||||
description: The name of the repository that the artifact belongs to
|
||||
digest:
|
||||
type: string
|
||||
description: The digest of the artifact
|
||||
@ -7252,6 +7340,10 @@ definitions:
|
||||
type: string
|
||||
description: 'The ID of the tag retention policy for the project'
|
||||
x-nullable: true
|
||||
proxy_speed_kb:
|
||||
type: string
|
||||
description: 'The bandwidth limit of proxy cache, in Kbps (kilobits per second). It limits the communication between Harbor and the upstream registry, not the client and the Harbor.'
|
||||
x-nullable: true
|
||||
ProjectSummary:
|
||||
type: object
|
||||
properties:
|
||||
@ -7754,6 +7846,9 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/RobotPermission'
|
||||
creator:
|
||||
type: string
|
||||
description: The creator of the robot
|
||||
creation_time:
|
||||
type: string
|
||||
format: date-time
|
||||
@ -8897,6 +8992,9 @@ definitions:
|
||||
ldap_group_search_scope:
|
||||
$ref: '#/definitions/IntegerConfigItem'
|
||||
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
||||
ldap_group_attach_parallel:
|
||||
$ref: '#/definitions/BoolConfigItem'
|
||||
description: Attach LDAP user group information in parallel.
|
||||
ldap_scope:
|
||||
$ref: '#/definitions/IntegerConfigItem'
|
||||
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
||||
@ -9087,6 +9185,11 @@ definitions:
|
||||
description: The scope to search ldap group. ''0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE''
|
||||
x-omitempty: true
|
||||
x-isnullable: true
|
||||
ldap_group_attach_parallel:
|
||||
type: boolean
|
||||
description: Attach LDAP user group information in parallel, the parallel worker count is 5
|
||||
x-omitempty: true
|
||||
x-isnullable: true
|
||||
ldap_scope:
|
||||
type: integer
|
||||
description: The scope to search ldap users,'0-LDAP_SCOPE_BASE, 1-LDAP_SCOPE_ONELEVEL, 2-LDAP_SCOPE_SUBTREE'
|
||||
|
5
make/migrations/postgresql/0150_2.12.0_schema.up.sql
Normal file
5
make/migrations/postgresql/0150_2.12.0_schema.up.sql
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
Add new column creator for robot table to add a new column to record the creator of the robot
|
||||
*/
|
||||
ALTER TABLE robot ADD COLUMN IF NOT EXISTS creator varchar(255);
|
||||
UPDATE robot SET creator = 'unknown' WHERE creator IS NULL;
|
@ -1,4 +1,3 @@
|
||||
version: '2.3'
|
||||
services:
|
||||
log:
|
||||
image: goharbor/harbor-log:{{version}}
|
||||
|
@ -101,6 +101,9 @@ http {
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_request_buffering off;
|
||||
|
||||
proxy_send_timeout 900;
|
||||
proxy_read_timeout 900;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
|
@ -9,6 +9,17 @@ packages:
|
||||
Controller:
|
||||
config:
|
||||
dir: testing/controller/artifact
|
||||
github.com/goharbor/harbor/src/controller/artifact/processor:
|
||||
interfaces:
|
||||
Processor:
|
||||
config:
|
||||
dir: testing/pkg/processor
|
||||
github.com/goharbor/harbor/src/controller/artifact/annotation:
|
||||
interfaces:
|
||||
Parser:
|
||||
config:
|
||||
dir: testing/pkg/parser
|
||||
outpkg: parser
|
||||
github.com/goharbor/harbor/src/controller/blob:
|
||||
interfaces:
|
||||
Controller:
|
||||
@ -188,6 +199,11 @@ packages:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/artifact
|
||||
github.com/goharbor/harbor/src/pkg/artifactrash:
|
||||
interfaces:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/artifactrash
|
||||
github.com/goharbor/harbor/src/pkg/blob:
|
||||
interfaces:
|
||||
Manager:
|
||||
@ -218,6 +234,11 @@ packages:
|
||||
Handler:
|
||||
config:
|
||||
dir: testing/pkg/scan
|
||||
github.com/goharbor/harbor/src/pkg/scan/postprocessors:
|
||||
interfaces:
|
||||
NativeScanReportConverter:
|
||||
config:
|
||||
dir: testing/pkg/scan/postprocessors
|
||||
github.com/goharbor/harbor/src/pkg/scan/report:
|
||||
interfaces:
|
||||
Manager:
|
||||
@ -238,7 +259,7 @@ packages:
|
||||
dir: pkg/scheduler
|
||||
outpkg: scheduler
|
||||
mockname: mockDAO
|
||||
filename: mock_dao_test.go
|
||||
filename: mock_dao_test.go
|
||||
inpackage: True
|
||||
Scheduler:
|
||||
config:
|
||||
@ -342,6 +363,14 @@ packages:
|
||||
DAO:
|
||||
config:
|
||||
dir: testing/pkg/immutable/dao
|
||||
github.com/goharbor/harbor/src/pkg/immutable/match:
|
||||
interfaces:
|
||||
ImmutableTagMatcher:
|
||||
config:
|
||||
dir: testing/pkg/immutable
|
||||
filename: matcher.go
|
||||
outpkg: immutable
|
||||
mockname: FakeMatcher
|
||||
github.com/goharbor/harbor/src/pkg/ldap:
|
||||
interfaces:
|
||||
Manager:
|
||||
@ -505,20 +534,36 @@ packages:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/securityhub
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
github.com/goharbor/harbor/src/pkg/tag:
|
||||
interfaces:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/tag
|
||||
github.com/goharbor/harbor/src/pkg/p2p/preheat/policy:
|
||||
interfaces:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/p2p/preheat/policy
|
||||
github.com/goharbor/harbor/src/pkg/p2p/preheat/instance:
|
||||
interfaces:
|
||||
Manager:
|
||||
config:
|
||||
dir: testing/pkg/p2p/preheat/instance
|
||||
github.com/goharbor/harbor/src/pkg/chart:
|
||||
interfaces:
|
||||
Operator:
|
||||
config:
|
||||
dir: testing/pkg/chart
|
||||
# registryctl related mocks
|
||||
github.com/goharbor/harbor/src/registryctl/client:
|
||||
interfaces:
|
||||
Client:
|
||||
config:
|
||||
dir: testing/registryctl
|
||||
outpkg: registryctl
|
||||
# remote interfaces
|
||||
github.com/docker/distribution:
|
||||
interfaces:
|
||||
Manifest:
|
||||
config:
|
||||
dir: testing/pkg/distribution
|
||||
|
@ -1,6 +0,0 @@
|
||||
# Migrate Patch
|
||||
This is a simple program to fix the breakage that was introduced by migrate in notary.
|
||||
## Usage
|
||||
```sh
|
||||
patch -database <db_url>
|
||||
```
|
@ -1,88 +0,0 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"flag"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/jackc/pgx/v4/stdlib" // registry pgx driver
|
||||
)
|
||||
|
||||
var dbURL string
|
||||
|
||||
const pgSQLAlterStmt string = `ALTER TABLE schema_migrations ADD COLUMN "dirty" boolean NOT NULL DEFAULT false`
|
||||
const pgSQLCheckColStmt string = `SELECT T1.C1, T2.C2 FROM
|
||||
(SELECT COUNT(*) AS C1 FROM information_schema.tables WHERE table_name='schema_migrations') T1,
|
||||
(SELECT COUNT(*) AS C2 FROM information_schema.columns WHERE table_name='schema_migrations' and column_name='dirty') T2`
|
||||
const pgSQLDelRows string = `DELETE FROM schema_migrations t WHERE t.version < ( SELECT MAX(version) FROM schema_migrations )`
|
||||
|
||||
func init() {
|
||||
urlUsage := `The URL to the target database (driver://url). Currently it only supports postgres`
|
||||
flag.StringVar(&dbURL, "database", "", urlUsage)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.Printf("Updating database.")
|
||||
if !strings.HasPrefix(dbURL, "postgres://") {
|
||||
log.Fatalf("Invalid URL: '%s'\n", dbURL)
|
||||
}
|
||||
db, err := sql.Open("pgx", dbURL)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to Database, error: %v\n", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
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)
|
||||
}
|
||||
}()
|
||||
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 {
|
||||
log.Fatalf("Failed to check schema_migrations table, error: %v \n", err)
|
||||
}
|
||||
if tblCount == 0 {
|
||||
log.Println("schema_migrations table does not exist, skip.")
|
||||
return
|
||||
}
|
||||
if colCount > 0 {
|
||||
log.Println("schema_migrations table does not require update, skip.")
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(pgSQLDelRows); err != nil {
|
||||
log.Fatalf("Failed to clean up table, error: %v", err)
|
||||
}
|
||||
if _, err := db.Exec(pgSQLAlterStmt); err != nil {
|
||||
log.Fatalf("Failed to update database, error: %v \n", err)
|
||||
}
|
||||
log.Println("Done updating database.")
|
||||
}
|
@ -134,6 +134,7 @@ const (
|
||||
OIDCGroupType = 3
|
||||
LDAPGroupAdminDn = "ldap_group_admin_dn"
|
||||
LDAPGroupMembershipAttribute = "ldap_group_membership_attribute"
|
||||
LDAPGroupAttachParallel = "ldap_group_attach_parallel"
|
||||
DefaultRegistryControllerEndpoint = "http://registryctl:8080"
|
||||
DefaultPortalURL = "http://portal:8080"
|
||||
DefaultRegistryCtlURL = "http://registryctl:8080"
|
||||
|
@ -118,6 +118,8 @@ type Controller interface {
|
||||
Walk(ctx context.Context, root *Artifact, walkFn func(*Artifact) error, option *Option) error
|
||||
// HasUnscannableLayer check artifact with digest if has unscannable layer
|
||||
HasUnscannableLayer(ctx context.Context, dgst string) (bool, error)
|
||||
// ListWithLatest list the artifacts when the latest_in_repository in the query was set
|
||||
ListWithLatest(ctx context.Context, query *q.Query, option *Option) (artifacts []*Artifact, err error)
|
||||
}
|
||||
|
||||
// NewController creates an instance of the default artifact controller
|
||||
@ -171,16 +173,18 @@ func (c *controller) Ensure(ctx context.Context, repository, digest string, opti
|
||||
}
|
||||
}
|
||||
}
|
||||
// fire event
|
||||
e := &metadata.PushArtifactEventMetadata{
|
||||
Ctx: ctx,
|
||||
Artifact: artifact,
|
||||
}
|
||||
if created {
|
||||
// fire event for create
|
||||
e := &metadata.PushArtifactEventMetadata{
|
||||
Ctx: ctx,
|
||||
Artifact: artifact,
|
||||
}
|
||||
|
||||
if option != nil && len(option.Tags) > 0 {
|
||||
e.Tag = option.Tags[0]
|
||||
if option != nil && len(option.Tags) > 0 {
|
||||
e.Tag = option.Tags[0]
|
||||
}
|
||||
notification.AddEvent(ctx, e)
|
||||
}
|
||||
notification.AddEvent(ctx, e)
|
||||
return created, artifact.ID, nil
|
||||
}
|
||||
|
||||
@ -782,3 +786,16 @@ func (c *controller) HasUnscannableLayer(ctx context.Context, dgst string) (bool
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ListWithLatest ...
|
||||
func (c *controller) ListWithLatest(ctx context.Context, query *q.Query, option *Option) (artifacts []*Artifact, err error) {
|
||||
arts, err := c.artMgr.ListWithLatest(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res []*Artifact
|
||||
for _, art := range arts {
|
||||
res = append(res, c.assembleArtifact(ctx, art, option))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ type controllerTestSuite struct {
|
||||
ctl *controller
|
||||
repoMgr *repotesting.Manager
|
||||
artMgr *arttesting.Manager
|
||||
artrashMgr *artrashtesting.FakeManager
|
||||
artrashMgr *artrashtesting.Manager
|
||||
blobMgr *blob.Manager
|
||||
tagCtl *tagtesting.FakeController
|
||||
labelMgr *label.Manager
|
||||
@ -80,7 +80,7 @@ type controllerTestSuite struct {
|
||||
func (c *controllerTestSuite) SetupTest() {
|
||||
c.repoMgr = &repotesting.Manager{}
|
||||
c.artMgr = &arttesting.Manager{}
|
||||
c.artrashMgr = &artrashtesting.FakeManager{}
|
||||
c.artrashMgr = &artrashtesting.Manager{}
|
||||
c.blobMgr = &blob.Manager{}
|
||||
c.tagCtl = &tagtesting.FakeController{}
|
||||
c.labelMgr = &label.Manager{}
|
||||
@ -323,6 +323,44 @@ func (c *controllerTestSuite) TestList() {
|
||||
c.Equal(0, len(artifacts[0].Accessories))
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestListWithLatest() {
|
||||
query := &q.Query{}
|
||||
option := &Option{
|
||||
WithTag: true,
|
||||
WithAccessory: true,
|
||||
}
|
||||
c.artMgr.On("ListWithLatest", mock.Anything, mock.Anything).Return([]*artifact.Artifact{
|
||||
{
|
||||
ID: 1,
|
||||
RepositoryID: 1,
|
||||
},
|
||||
}, nil)
|
||||
c.tagCtl.On("List").Return([]*tag.Tag{
|
||||
{
|
||||
Tag: model_tag.Tag{
|
||||
ID: 1,
|
||||
RepositoryID: 1,
|
||||
ArtifactID: 1,
|
||||
Name: "latest",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
c.repoMgr.On("Get", mock.Anything, mock.Anything).Return(&repomodel.RepoRecord{
|
||||
Name: "library/hello-world",
|
||||
}, nil)
|
||||
c.repoMgr.On("List", mock.Anything, mock.Anything).Return([]*repomodel.RepoRecord{
|
||||
{RepositoryID: 1, Name: "library/hello-world"},
|
||||
}, nil)
|
||||
c.accMgr.On("List", mock.Anything, mock.Anything).Return([]accessorymodel.Accessory{}, nil)
|
||||
artifacts, err := c.ctl.ListWithLatest(nil, query, option)
|
||||
c.Require().Nil(err)
|
||||
c.Require().Len(artifacts, 1)
|
||||
c.Equal(int64(1), artifacts[0].ID)
|
||||
c.Require().Len(artifacts[0].Tags, 1)
|
||||
c.Equal(int64(1), artifacts[0].Tags[0].ID)
|
||||
c.Equal(0, len(artifacts[0].Accessories))
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestGet() {
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything, mock.Anything).Return(&artifact.Artifact{
|
||||
ID: 1,
|
||||
@ -476,7 +514,7 @@ func (c *controllerTestSuite) TestDeleteDeeply() {
|
||||
},
|
||||
}, nil)
|
||||
c.repoMgr.On("Get", mock.Anything, mock.Anything).Return(&repomodel.RepoRecord{}, nil)
|
||||
c.artrashMgr.On("Create").Return(0, nil)
|
||||
c.artrashMgr.On("Create", mock.Anything, mock.Anything).Return(int64(0), nil)
|
||||
c.accMgr.On("List", mock.Anything, mock.Anything).Return([]accessorymodel.Accessory{}, nil)
|
||||
err = c.ctl.deleteDeeply(orm.NewContext(nil, &ormtesting.FakeOrmer{}), 1, false, false)
|
||||
c.Require().Nil(err)
|
||||
@ -534,7 +572,7 @@ func (c *controllerTestSuite) TestDeleteDeeply() {
|
||||
c.blobMgr.On("List", mock.Anything, mock.Anything).Return(nil, nil)
|
||||
c.blobMgr.On("CleanupAssociationsForProject", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
c.repoMgr.On("Get", mock.Anything, mock.Anything).Return(&repomodel.RepoRecord{}, nil)
|
||||
c.artrashMgr.On("Create").Return(0, nil)
|
||||
c.artrashMgr.On("Create", mock.Anything, mock.Anything).Return(int64(0), nil)
|
||||
err = c.ctl.deleteDeeply(orm.NewContext(nil, &ormtesting.FakeOrmer{}), 1, true, true)
|
||||
c.Require().Nil(err)
|
||||
|
||||
|
@ -102,8 +102,9 @@ type AdditionLink struct {
|
||||
|
||||
// Option is used to specify the properties returned when listing/getting artifacts
|
||||
type Option struct {
|
||||
WithTag bool
|
||||
TagOption *tag.Option // only works when WithTag is set to true
|
||||
WithLabel bool
|
||||
WithAccessory bool
|
||||
WithTag bool
|
||||
TagOption *tag.Option // only works when WithTag is set to true
|
||||
WithLabel bool
|
||||
WithAccessory bool
|
||||
LatestInRepository bool
|
||||
}
|
||||
|
@ -64,12 +64,12 @@ type processorTestSuite struct {
|
||||
suite.Suite
|
||||
processor *processor
|
||||
regCli *registry.Client
|
||||
chartOptr *chart.FakeOpertaor
|
||||
chartOptr *chart.Operator
|
||||
}
|
||||
|
||||
func (p *processorTestSuite) SetupTest() {
|
||||
p.regCli = ®istry.Client{}
|
||||
p.chartOptr = &chart.FakeOpertaor{}
|
||||
p.chartOptr = &chart.Operator{}
|
||||
p.processor = &processor{
|
||||
chartOperator: p.chartOptr,
|
||||
}
|
||||
@ -106,7 +106,7 @@ func (p *processorTestSuite) TestAbstractAddition() {
|
||||
p.Require().Nil(err)
|
||||
p.regCli.On("PullManifest", mock.Anything, mock.Anything).Return(manifest, "", nil)
|
||||
p.regCli.On("PullBlob", mock.Anything, mock.Anything).Return(int64(0), io.NopCloser(strings.NewReader(chartYaml)), nil)
|
||||
p.chartOptr.On("GetDetails").Return(chartDetails, nil)
|
||||
p.chartOptr.On("GetDetails", mock.Anything).Return(chartDetails, nil)
|
||||
|
||||
// values.yaml
|
||||
addition, err := p.processor.AbstractAddition(nil, artifact, AdditionTypeValues)
|
||||
|
@ -45,7 +45,8 @@ func (h *Handler) Handle(ctx context.Context, value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case *event.PushArtifactEvent, *event.DeleteArtifactEvent,
|
||||
*event.DeleteRepositoryEvent, *event.CreateProjectEvent, *event.DeleteProjectEvent,
|
||||
*event.DeleteTagEvent, *event.CreateTagEvent:
|
||||
*event.DeleteTagEvent, *event.CreateTagEvent,
|
||||
*event.CreateRobotEvent, *event.DeleteRobotEvent:
|
||||
addAuditLog = true
|
||||
case *event.PullArtifactEvent:
|
||||
addAuditLog = !config.PullAuditLogDisable(ctx)
|
||||
|
@ -65,6 +65,8 @@ func init() {
|
||||
_ = notifier.Subscribe(event.TopicDeleteRepository, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicCreateTag, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicDeleteTag, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicCreateRobot, &auditlog.Handler{})
|
||||
_ = notifier.Subscribe(event.TopicDeleteRobot, &auditlog.Handler{})
|
||||
|
||||
// internal
|
||||
_ = notifier.Subscribe(event.TopicPullArtifact, &internal.ArtifactEventHandler{})
|
||||
|
73
src/controller/event/metadata/robot.go
Normal file
73
src/controller/event/metadata/robot.go
Normal file
@ -0,0 +1,73 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
event2 "github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
// CreateRobotEventMetadata is the metadata from which the create robot event can be resolved
|
||||
type CreateRobotEventMetadata struct {
|
||||
Ctx context.Context
|
||||
Robot *model.Robot
|
||||
}
|
||||
|
||||
// Resolve to the event from the metadata
|
||||
func (c *CreateRobotEventMetadata) Resolve(event *event.Event) error {
|
||||
data := &event2.CreateRobotEvent{
|
||||
EventType: event2.TopicCreateRobot,
|
||||
Robot: c.Robot,
|
||||
OccurAt: time.Now(),
|
||||
}
|
||||
cx, exist := security.FromContext(c.Ctx)
|
||||
if exist {
|
||||
data.Operator = cx.GetUsername()
|
||||
}
|
||||
data.Robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(c.Ctx), data.Robot.Name)
|
||||
event.Topic = event2.TopicCreateRobot
|
||||
event.Data = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRobotEventMetadata is the metadata from which the delete robot event can be resolved
|
||||
type DeleteRobotEventMetadata struct {
|
||||
Ctx context.Context
|
||||
Robot *model.Robot
|
||||
}
|
||||
|
||||
// Resolve to the event from the metadata
|
||||
func (d *DeleteRobotEventMetadata) Resolve(event *event.Event) error {
|
||||
data := &event2.DeleteRobotEvent{
|
||||
EventType: event2.TopicDeleteRobot,
|
||||
Robot: d.Robot,
|
||||
OccurAt: time.Now(),
|
||||
}
|
||||
cx, exist := security.FromContext(d.Ctx)
|
||||
if exist {
|
||||
data.Operator = cx.GetUsername()
|
||||
}
|
||||
data.Robot.Name = fmt.Sprintf("%s%s", config.RobotPrefix(d.Ctx), data.Robot.Name)
|
||||
event.Topic = event2.TopicDeleteRobot
|
||||
event.Data = data
|
||||
return nil
|
||||
}
|
83
src/controller/event/metadata/robot_test.go
Normal file
83
src/controller/event/metadata/robot_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
// 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 metadata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
event2 "github.com/goharbor/harbor/src/controller/event"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/notifier/event"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
)
|
||||
|
||||
type robotEventTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (t *tagEventTestSuite) TestResolveOfCreateRobotEventMetadata() {
|
||||
cfg := map[string]interface{}{
|
||||
common.RobotPrefix: "robot$",
|
||||
}
|
||||
config.InitWithSettings(cfg)
|
||||
|
||||
e := &event.Event{}
|
||||
metadata := &CreateRobotEventMetadata{
|
||||
Ctx: context.Background(),
|
||||
Robot: &model.Robot{
|
||||
ID: 1,
|
||||
Name: "test",
|
||||
},
|
||||
}
|
||||
err := metadata.Resolve(e)
|
||||
t.Require().Nil(err)
|
||||
t.Equal(event2.TopicCreateRobot, e.Topic)
|
||||
t.Require().NotNil(e.Data)
|
||||
data, ok := e.Data.(*event2.CreateRobotEvent)
|
||||
t.Require().True(ok)
|
||||
t.Equal(int64(1), data.Robot.ID)
|
||||
t.Equal("robot$test", data.Robot.Name)
|
||||
}
|
||||
|
||||
func (t *tagEventTestSuite) TestResolveOfDeleteRobotEventMetadata() {
|
||||
cfg := map[string]interface{}{
|
||||
common.RobotPrefix: "robot$",
|
||||
}
|
||||
config.InitWithSettings(cfg)
|
||||
|
||||
e := &event.Event{}
|
||||
metadata := &DeleteRobotEventMetadata{
|
||||
Ctx: context.Background(),
|
||||
Robot: &model.Robot{
|
||||
ID: 1,
|
||||
},
|
||||
}
|
||||
err := metadata.Resolve(e)
|
||||
t.Require().Nil(err)
|
||||
t.Equal(event2.TopicDeleteRobot, e.Topic)
|
||||
t.Require().NotNil(e.Data)
|
||||
data, ok := e.Data.(*event2.DeleteRobotEvent)
|
||||
t.Require().True(ok)
|
||||
t.Equal(int64(1), data.Robot.ID)
|
||||
}
|
||||
|
||||
func TestRobotEventTestSuite(t *testing.T) {
|
||||
suite.Run(t, &robotEventTestSuite{})
|
||||
}
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/pkg/audit/model"
|
||||
proModels "github.com/goharbor/harbor/src/pkg/project/models"
|
||||
robotModel "github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
v1 "github.com/goharbor/harbor/src/pkg/scan/rest/v1"
|
||||
)
|
||||
|
||||
@ -47,6 +48,8 @@ const (
|
||||
TopicReplication = "REPLICATION"
|
||||
TopicArtifactLabeled = "ARTIFACT_LABELED"
|
||||
TopicTagRetention = "TAG_RETENTION"
|
||||
TopicCreateRobot = "CREATE_ROBOT"
|
||||
TopicDeleteRobot = "DELETE_ROBOT"
|
||||
)
|
||||
|
||||
// CreateProjectEvent is the creating project event
|
||||
@ -369,3 +372,53 @@ func (r *RetentionEvent) String() string {
|
||||
return fmt.Sprintf("TaskID-%d Status-%s Deleted-%s OccurAt-%s",
|
||||
r.TaskID, r.Status, candidates, r.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// CreateRobotEvent is the creating robot event
|
||||
type CreateRobotEvent struct {
|
||||
EventType string
|
||||
Robot *robotModel.Robot
|
||||
Operator string
|
||||
OccurAt time.Time
|
||||
}
|
||||
|
||||
// ResolveToAuditLog ...
|
||||
func (c *CreateRobotEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
||||
auditLog := &model.AuditLog{
|
||||
ProjectID: c.Robot.ProjectID,
|
||||
OpTime: c.OccurAt,
|
||||
Operation: rbac.ActionCreate.String(),
|
||||
Username: c.Operator,
|
||||
ResourceType: "robot",
|
||||
Resource: c.Robot.Name}
|
||||
return auditLog, nil
|
||||
}
|
||||
|
||||
func (c *CreateRobotEvent) String() string {
|
||||
return fmt.Sprintf("Name-%s Operator-%s OccurAt-%s",
|
||||
c.Robot.Name, c.Operator, c.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// DeleteRobotEvent is the deleting robot event
|
||||
type DeleteRobotEvent struct {
|
||||
EventType string
|
||||
Robot *robotModel.Robot
|
||||
Operator string
|
||||
OccurAt time.Time
|
||||
}
|
||||
|
||||
// ResolveToAuditLog ...
|
||||
func (c *DeleteRobotEvent) ResolveToAuditLog() (*model.AuditLog, error) {
|
||||
auditLog := &model.AuditLog{
|
||||
ProjectID: c.Robot.ProjectID,
|
||||
OpTime: c.OccurAt,
|
||||
Operation: rbac.ActionDelete.String(),
|
||||
Username: c.Operator,
|
||||
ResourceType: "robot",
|
||||
Resource: c.Robot.Name}
|
||||
return auditLog, nil
|
||||
}
|
||||
|
||||
func (c *DeleteRobotEvent) String() string {
|
||||
return fmt.Sprintf("Name-%s Operator-%s OccurAt-%s",
|
||||
c.Robot.Name, c.Operator, c.OccurAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ type preheatSuite struct {
|
||||
suite.Suite
|
||||
ctx context.Context
|
||||
controller Controller
|
||||
fakeInstanceMgr *instance.FakeManager
|
||||
fakePolicyMgr *pmocks.FakeManager
|
||||
fakeInstanceMgr *instance.Manager
|
||||
fakePolicyMgr *pmocks.Manager
|
||||
fakeScheduler *smocks.Scheduler
|
||||
mockInstanceServer *httptest.Server
|
||||
fakeExecutionMgr *tmocks.ExecutionManager
|
||||
@ -40,8 +40,8 @@ type preheatSuite struct {
|
||||
|
||||
func TestPreheatSuite(t *testing.T) {
|
||||
t.Log("Start TestPreheatSuite")
|
||||
fakeInstanceMgr := &instance.FakeManager{}
|
||||
fakePolicyMgr := &pmocks.FakeManager{}
|
||||
fakeInstanceMgr := &instance.Manager{}
|
||||
fakePolicyMgr := &pmocks.Manager{}
|
||||
fakeScheduler := &smocks.Scheduler{}
|
||||
fakeExecutionMgr := &tmocks.ExecutionManager{}
|
||||
|
||||
|
@ -70,7 +70,7 @@ func (suite *EnforcerTestSuite) SetupSuite() {
|
||||
suite.server.StartTLS()
|
||||
|
||||
fakePolicies := mockPolicies()
|
||||
fakePolicyManager := &policy.FakeManager{}
|
||||
fakePolicyManager := &policy.Manager{}
|
||||
fakePolicyManager.On("Get",
|
||||
context.TODO(),
|
||||
mock.AnythingOfType("int64")).
|
||||
@ -130,7 +130,7 @@ func (suite *EnforcerTestSuite) SetupSuite() {
|
||||
},
|
||||
}, nil)
|
||||
|
||||
fakeInstanceMgr := &instance.FakeManager{}
|
||||
fakeInstanceMgr := &instance.Manager{}
|
||||
fakeInstanceMgr.On("Get",
|
||||
context.TODO(),
|
||||
mock.AnythingOfType("int64"),
|
||||
|
@ -264,7 +264,7 @@ func (c *controller) HeadManifest(_ context.Context, art lib.ArtifactInfo, remot
|
||||
func (c *controller) ProxyBlob(ctx context.Context, p *proModels.Project, art lib.ArtifactInfo) (int64, io.ReadCloser, error) {
|
||||
remoteRepo := getRemoteRepo(art)
|
||||
log.Debugf("The blob doesn't exist, proxy the request to the target server, url:%v", remoteRepo)
|
||||
rHelper, err := NewRemoteHelper(ctx, p.RegistryID)
|
||||
rHelper, err := NewRemoteHelper(ctx, p.RegistryID, WithSpeed(p.ProxyCacheSpeed()))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
37
src/controller/proxy/options.go
Normal file
37
src/controller/proxy/options.go
Normal file
@ -0,0 +1,37 @@
|
||||
// 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 proxy
|
||||
|
||||
type Option func(*Options)
|
||||
|
||||
type Options struct {
|
||||
// Speed is the data transfer speed for proxy cache from Harbor to upstream registry, no limit by default.
|
||||
Speed int32
|
||||
}
|
||||
|
||||
func NewOptions(opts ...Option) *Options {
|
||||
o := &Options{}
|
||||
for _, opt := range opts {
|
||||
opt(o)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func WithSpeed(speed int32) Option {
|
||||
return func(o *Options) {
|
||||
o.Speed = speed
|
||||
}
|
||||
}
|
33
src/controller/proxy/options_test.go
Normal file
33
src/controller/proxy/options_test.go
Normal file
@ -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 proxy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewOptions(t *testing.T) {
|
||||
// test default options
|
||||
o := NewOptions()
|
||||
assert.Equal(t, int32(0), o.Speed)
|
||||
|
||||
// test with options
|
||||
// with speed
|
||||
withSpeed := WithSpeed(1024)
|
||||
o = NewOptions(withSpeed)
|
||||
assert.Equal(t, int32(1024), o.Speed)
|
||||
}
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"github.com/goharbor/harbor/src/pkg/reg"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/adapter"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/model"
|
||||
@ -43,13 +44,16 @@ type remoteHelper struct {
|
||||
regID int64
|
||||
registry adapter.ArtifactRegistry
|
||||
registryMgr reg.Manager
|
||||
opts *Options
|
||||
}
|
||||
|
||||
// NewRemoteHelper create a remote interface
|
||||
func NewRemoteHelper(ctx context.Context, regID int64) (RemoteInterface, error) {
|
||||
func NewRemoteHelper(ctx context.Context, regID int64, opts ...Option) (RemoteInterface, error) {
|
||||
r := &remoteHelper{
|
||||
regID: regID,
|
||||
registryMgr: reg.Mgr}
|
||||
registryMgr: reg.Mgr,
|
||||
opts: NewOptions(opts...),
|
||||
}
|
||||
if err := r.init(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -83,7 +87,14 @@ func (r *remoteHelper) init(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *remoteHelper) BlobReader(repo, dig string) (int64, io.ReadCloser, error) {
|
||||
return r.registry.PullBlob(repo, dig)
|
||||
sz, bReader, err := r.registry.PullBlob(repo, dig)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if r.opts != nil && r.opts.Speed > 0 {
|
||||
bReader = lib.NewReader(bReader, r.opts.Speed)
|
||||
}
|
||||
return sz, bReader, err
|
||||
}
|
||||
|
||||
func (r *remoteHelper) Manifest(repo string, ref string) (distribution.Manifest, string, error) {
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
|
||||
common_http "github.com/goharbor/harbor/src/common/http"
|
||||
trans "github.com/goharbor/harbor/src/controller/replication/transfer"
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/adapter"
|
||||
"github.com/goharbor/harbor/src/pkg/reg/model"
|
||||
@ -380,7 +381,7 @@ func (t *transfer) copyBlobByMonolithic(srcRepo, dstRepo, digest string, sizeFro
|
||||
return err
|
||||
}
|
||||
if speed > 0 {
|
||||
data = trans.NewReader(data, speed)
|
||||
data = lib.NewReader(data, speed)
|
||||
}
|
||||
defer data.Close()
|
||||
// get size 0 from PullBlob, use size from distribution.Descriptor instead.
|
||||
@ -435,7 +436,7 @@ func (t *transfer) copyBlobByChunk(srcRepo, dstRepo, digest string, sizeFromDesc
|
||||
}
|
||||
|
||||
if speed > 0 {
|
||||
data = trans.NewReader(data, speed)
|
||||
data = lib.NewReader(data, speed)
|
||||
}
|
||||
// failureEnd will only be used for adjusting content range when issue happened during push the chunk.
|
||||
var failureEnd int64
|
||||
|
@ -23,12 +23,14 @@ import (
|
||||
|
||||
rbac_project "github.com/goharbor/harbor/src/common/rbac/project"
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"github.com/goharbor/harbor/src/controller/event/metadata"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
"github.com/goharbor/harbor/src/lib/log"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/lib/retry"
|
||||
"github.com/goharbor/harbor/src/pkg"
|
||||
"github.com/goharbor/harbor/src/pkg/notification"
|
||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||
"github.com/goharbor/harbor/src/pkg/project"
|
||||
"github.com/goharbor/harbor/src/pkg/rbac"
|
||||
@ -121,7 +123,8 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
if r.Level == LEVELPROJECT {
|
||||
name = fmt.Sprintf("%s+%s", r.ProjectName, r.Name)
|
||||
}
|
||||
robotID, err := d.robotMgr.Create(ctx, &model.Robot{
|
||||
|
||||
rCreate := &model.Robot{
|
||||
Name: name,
|
||||
Description: r.Description,
|
||||
ProjectID: r.ProjectID,
|
||||
@ -130,7 +133,9 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
Duration: r.Duration,
|
||||
Salt: salt,
|
||||
Visible: r.Visible,
|
||||
})
|
||||
Creator: r.Creator,
|
||||
}
|
||||
robotID, err := d.robotMgr.Create(ctx, rCreate)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
@ -138,17 +143,31 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
||||
if err := d.createPermission(ctx, r); err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
// fire event
|
||||
notification.AddEvent(ctx, &metadata.CreateRobotEventMetadata{
|
||||
Ctx: ctx,
|
||||
Robot: rCreate,
|
||||
})
|
||||
return robotID, pwd, nil
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (d *controller) Delete(ctx context.Context, id int64) error {
|
||||
rDelete, err := d.robotMgr.Get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.robotMgr.Delete(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.rbacMgr.DeletePermissionsByRole(ctx, ROBOTTYPE, id); err != nil {
|
||||
return err
|
||||
}
|
||||
// fire event
|
||||
notification.AddEvent(ctx, &metadata.DeleteRobotEventMetadata{
|
||||
Ctx: ctx,
|
||||
Robot: rDelete,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
"github.com/goharbor/harbor/src/common/utils/test"
|
||||
"github.com/goharbor/harbor/src/lib/config"
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
rbac_model "github.com/goharbor/harbor/src/pkg/rbac/model"
|
||||
"github.com/goharbor/harbor/src/pkg/robot/model"
|
||||
htesting "github.com/goharbor/harbor/src/testing"
|
||||
testsec "github.com/goharbor/harbor/src/testing/common/security"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/project"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/rbac"
|
||||
@ -102,7 +104,9 @@ func (suite *ControllerTestSuite) TestCreate() {
|
||||
robotMgr := &robot.Manager{}
|
||||
|
||||
c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
|
||||
ctx := context.TODO()
|
||||
secCtx := &testsec.Context{}
|
||||
secCtx.On("GetUsername").Return("security-context-user")
|
||||
ctx := security.NewContext(context.Background(), secCtx)
|
||||
projectMgr.On("Get", mock.Anything, mock.Anything).Return(&proModels.Project{ProjectID: 1, Name: "library"}, nil)
|
||||
robotMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
rbacMgr.On("CreateRbacPolicy", mock.Anything, mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
@ -145,6 +149,12 @@ func (suite *ControllerTestSuite) TestDelete() {
|
||||
c := controller{robotMgr: robotMgr, rbacMgr: rbacMgr, proMgr: projectMgr}
|
||||
ctx := context.TODO()
|
||||
|
||||
robotMgr.On("Get", mock.Anything, mock.Anything).Return(&model.Robot{
|
||||
Name: "library+test",
|
||||
Description: "test get method",
|
||||
ProjectID: 1,
|
||||
Secret: utils.RandStringBytes(10),
|
||||
}, nil)
|
||||
robotMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
rbacMgr.On("DeletePermissionsByRole", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
|
@ -864,6 +864,7 @@ func (bc *basicController) makeRobotAccount(ctx context.Context, projectID int64
|
||||
Description: "for scan",
|
||||
ProjectID: projectID,
|
||||
Duration: -1,
|
||||
Creator: "harbor-core-for-scan-all",
|
||||
},
|
||||
Level: robot.LEVELPROJECT,
|
||||
Permissions: []*robot.Permission{
|
||||
|
@ -82,7 +82,7 @@ type ControllerTestSuite struct {
|
||||
reportMgr *reporttesting.Manager
|
||||
ar artifact.Controller
|
||||
c *basicController
|
||||
reportConverter *postprocessorstesting.ScanReportV1ToV2Converter
|
||||
reportConverter *postprocessorstesting.NativeScanReportConverter
|
||||
cache *mockcache.Cache
|
||||
}
|
||||
|
||||
@ -235,6 +235,7 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
||||
Description: "for scan",
|
||||
ProjectID: suite.artifact.ProjectID,
|
||||
Duration: -1,
|
||||
Creator: "harbor-core-for-scan-all",
|
||||
},
|
||||
Level: robot.LEVELPROJECT,
|
||||
Permissions: []*robot.Permission{
|
||||
@ -266,6 +267,7 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
||||
Description: "for scan",
|
||||
ProjectID: suite.artifact.ProjectID,
|
||||
Duration: -1,
|
||||
Creator: "harbor-core-for-scan-all",
|
||||
},
|
||||
Level: "project",
|
||||
}, nil)
|
||||
@ -339,7 +341,7 @@ func (suite *ControllerTestSuite) SetupSuite() {
|
||||
|
||||
execMgr: suite.execMgr,
|
||||
taskMgr: suite.taskMgr,
|
||||
reportConverter: &postprocessorstesting.ScanReportV1ToV2Converter{},
|
||||
reportConverter: &postprocessorstesting.NativeScanReportConverter{},
|
||||
cache: func() cache.Cache { return suite.cache },
|
||||
}
|
||||
mock.OnAnything(suite.scanHandler, "JobVendorType").Return("IMAGE_SCAN")
|
||||
@ -486,6 +488,7 @@ func (suite *ControllerTestSuite) TestScanControllerGetReport() {
|
||||
{ExtraAttrs: suite.makeExtraAttrs(int64(1), "rp-uuid-001")},
|
||||
}, nil).Once()
|
||||
mock.OnAnything(suite.accessoryMgr, "List").Return(nil, nil)
|
||||
mock.OnAnything(suite.c.reportConverter, "FromRelationalSchema").Return("", nil)
|
||||
rep, err := suite.c.GetReport(ctx, suite.artifact, []string{v1.MimeTypeNativeReport})
|
||||
require.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), 1, len(rep))
|
||||
|
@ -51,7 +51,7 @@ type CallbackTestSuite struct {
|
||||
scanCtl Controller
|
||||
|
||||
taskMgr *tasktesting.Manager
|
||||
reportConverter *postprocessorstesting.ScanReportV1ToV2Converter
|
||||
reportConverter *postprocessorstesting.NativeScanReportConverter
|
||||
}
|
||||
|
||||
func (suite *CallbackTestSuite) SetupSuite() {
|
||||
@ -69,7 +69,7 @@ func (suite *CallbackTestSuite) SetupSuite() {
|
||||
suite.taskMgr = &tasktesting.Manager{}
|
||||
taskMgr = suite.taskMgr
|
||||
|
||||
suite.reportConverter = &postprocessorstesting.ScanReportV1ToV2Converter{}
|
||||
suite.reportConverter = &postprocessorstesting.NativeScanReportConverter{}
|
||||
|
||||
suite.scanCtl = &basicController{
|
||||
makeCtx: context.TODO,
|
||||
|
@ -44,7 +44,7 @@ type ControllerTestSuite struct {
|
||||
c *controller
|
||||
scannerMgr *scannerMock.Manager
|
||||
secHubMgr *securityMock.Manager
|
||||
tagMgr *tagMock.FakeManager
|
||||
tagMgr *tagMock.Manager
|
||||
}
|
||||
|
||||
// TestController is the entry of controller test suite
|
||||
@ -56,7 +56,7 @@ func TestController(t *testing.T) {
|
||||
func (suite *ControllerTestSuite) SetupTest() {
|
||||
suite.secHubMgr = &securityMock.Manager{}
|
||||
suite.scannerMgr = &scannerMock.Manager{}
|
||||
suite.tagMgr = &tagMock.FakeManager{}
|
||||
suite.tagMgr = &tagMock.Manager{}
|
||||
|
||||
suite.c = &controller{
|
||||
secHubMgr: suite.secHubMgr,
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
@ -27,6 +26,7 @@ import (
|
||||
_ "github.com/goharbor/harbor/src/pkg/config/inmemory"
|
||||
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
ormtesting "github.com/goharbor/harbor/src/testing/lib/orm"
|
||||
"github.com/goharbor/harbor/src/testing/mock"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/artifact"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/immutable"
|
||||
"github.com/goharbor/harbor/src/testing/pkg/repository"
|
||||
@ -38,14 +38,14 @@ type controllerTestSuite struct {
|
||||
ctl *controller
|
||||
repoMgr *repository.Manager
|
||||
artMgr *artifact.Manager
|
||||
tagMgr *tagtesting.FakeManager
|
||||
tagMgr *tagtesting.Manager
|
||||
immutableMtr *immutable.FakeMatcher
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) SetupTest() {
|
||||
c.repoMgr = &repository.Manager{}
|
||||
c.artMgr = &artifact.Manager{}
|
||||
c.tagMgr = &tagtesting.FakeManager{}
|
||||
c.tagMgr = &tagtesting.Manager{}
|
||||
c.immutableMtr = &immutable.FakeMatcher{}
|
||||
c.ctl = &controller{
|
||||
tagMgr: c.tagMgr,
|
||||
@ -56,7 +56,7 @@ func (c *controllerTestSuite) SetupTest() {
|
||||
|
||||
func (c *controllerTestSuite) TestEnsureTag() {
|
||||
// the tag already exists under the repository and is attached to the artifact
|
||||
c.tagMgr.On("List").Return([]*tag.Tag{
|
||||
c.tagMgr.On("List", mock.Anything, mock.Anything).Return([]*tag.Tag{
|
||||
{
|
||||
ID: 1,
|
||||
RepositoryID: 1,
|
||||
@ -67,7 +67,7 @@ func (c *controllerTestSuite) TestEnsureTag() {
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(false, nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(false, nil)
|
||||
_, err := c.ctl.Ensure(orm.NewContext(nil, &ormtesting.FakeOrmer{}), 1, 1, "latest")
|
||||
c.Require().Nil(err)
|
||||
c.tagMgr.AssertExpectations(c.T())
|
||||
@ -76,7 +76,7 @@ func (c *controllerTestSuite) TestEnsureTag() {
|
||||
c.SetupTest()
|
||||
|
||||
// the tag exists under the repository, but it is attached to other artifact
|
||||
c.tagMgr.On("List").Return([]*tag.Tag{
|
||||
c.tagMgr.On("List", mock.Anything, mock.Anything).Return([]*tag.Tag{
|
||||
{
|
||||
ID: 1,
|
||||
RepositoryID: 1,
|
||||
@ -84,11 +84,11 @@ func (c *controllerTestSuite) TestEnsureTag() {
|
||||
Name: "latest",
|
||||
},
|
||||
}, nil)
|
||||
c.tagMgr.On("Update").Return(nil)
|
||||
c.tagMgr.On("Update", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(false, nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(false, nil)
|
||||
_, err = c.ctl.Ensure(orm.NewContext(nil, &ormtesting.FakeOrmer{}), 1, 1, "latest")
|
||||
c.Require().Nil(err)
|
||||
c.tagMgr.AssertExpectations(c.T())
|
||||
@ -97,26 +97,26 @@ func (c *controllerTestSuite) TestEnsureTag() {
|
||||
c.SetupTest()
|
||||
|
||||
// the tag doesn't exist under the repository, create it
|
||||
c.tagMgr.On("List").Return([]*tag.Tag{}, nil)
|
||||
c.tagMgr.On("Create").Return(1, nil)
|
||||
c.tagMgr.On("List", mock.Anything, mock.Anything).Return([]*tag.Tag{}, nil)
|
||||
c.tagMgr.On("Create", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(false, nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(false, nil)
|
||||
_, err = c.ctl.Ensure(orm.NewContext(nil, &ormtesting.FakeOrmer{}), 1, 1, "latest")
|
||||
c.Require().Nil(err)
|
||||
c.tagMgr.AssertExpectations(c.T())
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestCount() {
|
||||
c.tagMgr.On("Count").Return(1, nil)
|
||||
c.tagMgr.On("Count", mock.Anything, mock.Anything).Return(int64(1), nil)
|
||||
total, err := c.ctl.Count(nil, nil)
|
||||
c.Require().Nil(err)
|
||||
c.Equal(int64(1), total)
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestList() {
|
||||
c.tagMgr.On("List").Return([]*tag.Tag{
|
||||
c.tagMgr.On("List", mock.Anything, mock.Anything).Return([]*tag.Tag{
|
||||
{
|
||||
RepositoryID: 1,
|
||||
Name: "testlist",
|
||||
@ -134,7 +134,7 @@ func (c *controllerTestSuite) TestGet() {
|
||||
getTest.RepositoryID = 1
|
||||
getTest.Name = "testget"
|
||||
|
||||
c.tagMgr.On("Get").Return(getTest, nil)
|
||||
c.tagMgr.On("Get", mock.Anything, mock.Anything).Return(getTest, nil)
|
||||
tag, err := c.ctl.Get(nil, 1, nil)
|
||||
c.Require().Nil(err)
|
||||
c.tagMgr.AssertExpectations(c.T())
|
||||
@ -143,36 +143,36 @@ func (c *controllerTestSuite) TestGet() {
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestDelete() {
|
||||
c.tagMgr.On("Get").Return(&tag.Tag{
|
||||
c.tagMgr.On("Get", mock.Anything, mock.Anything).Return(&tag.Tag{
|
||||
RepositoryID: 1,
|
||||
Name: "test",
|
||||
}, nil)
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(false, nil)
|
||||
c.tagMgr.On("Delete").Return(nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(false, nil)
|
||||
c.tagMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
err := c.ctl.Delete(nil, 1)
|
||||
c.Require().Nil(err)
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestDeleteImmutable() {
|
||||
c.tagMgr.On("Get").Return(&tag.Tag{
|
||||
c.tagMgr.On("Get", mock.Anything, mock.Anything).Return(&tag.Tag{
|
||||
RepositoryID: 1,
|
||||
Name: "test",
|
||||
}, nil)
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(true, nil)
|
||||
c.tagMgr.On("Delete").Return(nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(true, nil)
|
||||
c.tagMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
err := c.ctl.Delete(nil, 1)
|
||||
c.Require().NotNil(err)
|
||||
c.True(errors.IsErr(err, errors.PreconditionCode))
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestUpdate() {
|
||||
c.tagMgr.On("Update").Return(nil)
|
||||
mock.OnAnything(c.tagMgr, "Update").Return(nil)
|
||||
err := c.ctl.Update(nil, &Tag{
|
||||
Tag: tag.Tag{
|
||||
RepositoryID: 1,
|
||||
@ -184,14 +184,14 @@ func (c *controllerTestSuite) TestUpdate() {
|
||||
}
|
||||
|
||||
func (c *controllerTestSuite) TestDeleteTags() {
|
||||
c.tagMgr.On("Get").Return(&tag.Tag{
|
||||
c.tagMgr.On("Get", mock.Anything, mock.Anything).Return(&tag.Tag{
|
||||
RepositoryID: 1,
|
||||
}, nil)
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(&pkg_artifact.Artifact{
|
||||
ID: 1,
|
||||
}, nil)
|
||||
c.immutableMtr.On("Match").Return(false, nil)
|
||||
c.tagMgr.On("Delete").Return(nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(false, nil)
|
||||
c.tagMgr.On("Delete", mock.Anything, mock.Anything).Return(nil)
|
||||
ids := []int64{1, 2, 3, 4}
|
||||
err := c.ctl.DeleteTags(nil, ids)
|
||||
c.Require().Nil(err)
|
||||
@ -218,7 +218,7 @@ func (c *controllerTestSuite) TestAssembleTag() {
|
||||
}
|
||||
|
||||
c.artMgr.On("Get", mock.Anything, mock.Anything).Return(art, nil)
|
||||
c.immutableMtr.On("Match").Return(true, nil)
|
||||
mock.OnAnything(c.immutableMtr, "Match").Return(true, nil)
|
||||
tag := c.ctl.assembleTag(nil, tg, option)
|
||||
c.Require().NotNil(tag)
|
||||
c.Equal(tag.ID, tg.ID)
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"strings"
|
||||
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
"github.com/goharbor/harbor/src/common/models"
|
||||
@ -38,6 +39,10 @@ import (
|
||||
ugModel "github.com/goharbor/harbor/src/pkg/usergroup/model"
|
||||
)
|
||||
|
||||
const (
|
||||
workerCount = 5
|
||||
)
|
||||
|
||||
// Auth implements AuthenticateHelper interface to authenticate against LDAP
|
||||
type Auth struct {
|
||||
auth.DefaultAuthenticateHelper
|
||||
@ -117,24 +122,94 @@ func (l *Auth) attachLDAPGroup(ctx context.Context, ldapUsers []model.User, u *m
|
||||
return
|
||||
}
|
||||
userGroups := make([]ugModel.UserGroup, 0)
|
||||
if groupCfg.AttachParallel {
|
||||
log.Debug("Attach LDAP group in parallel")
|
||||
l.attachGroupParallel(ctx, ldapUsers, u)
|
||||
return
|
||||
}
|
||||
// Attach LDAP group sequencially
|
||||
for _, dn := range ldapUsers[0].GroupDNList {
|
||||
lGroups, err := sess.SearchGroupByDN(dn)
|
||||
if err != nil {
|
||||
log.Warningf("Can not get the ldap group name with DN %v, error %v", dn, err)
|
||||
continue
|
||||
if lgroup, exist := verifyGroupInLDAP(dn, sess); exist {
|
||||
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lgroup.Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
||||
}
|
||||
if len(lGroups) == 0 {
|
||||
log.Warningf("Can not get the ldap group name with DN %v", dn)
|
||||
continue
|
||||
}
|
||||
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lGroups[0].Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
||||
}
|
||||
u.GroupIDs, err = ugCtl.Ctl.Populate(ctx, userGroups)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to fetch ldap group configuration:%v", err)
|
||||
log.Warningf("Failed to populate ldap group, error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Auth) attachGroupParallel(ctx context.Context, ldapUsers []model.User, u *models.User) {
|
||||
userGroupsList := make([][]ugModel.UserGroup, workerCount)
|
||||
gdsList := make([][]string, workerCount)
|
||||
// Divide the groupDNs into workerCount parts
|
||||
for index, dn := range ldapUsers[0].GroupDNList {
|
||||
idx := index % workerCount
|
||||
gdsList[idx] = append(gdsList[idx], dn)
|
||||
}
|
||||
g := new(errgroup.Group)
|
||||
g.SetLimit(workerCount)
|
||||
|
||||
for i := 0; i < workerCount; i++ {
|
||||
curIndex := i
|
||||
g.Go(func() error {
|
||||
userGroups := make([]ugModel.UserGroup, 0)
|
||||
groups := gdsList[curIndex]
|
||||
if len(groups) == 0 {
|
||||
return nil
|
||||
}
|
||||
// use different ldap session for each go routine
|
||||
ldapSession, err := ldapCtl.Ctl.Session(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ldapSession.Open(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer ldapSession.Close()
|
||||
log.Debugf("Current worker index is %v", curIndex)
|
||||
// verify and populate group
|
||||
for _, dn := range groups {
|
||||
if lgroup, exist := verifyGroupInLDAP(dn, ldapSession); exist {
|
||||
userGroups = append(userGroups, ugModel.UserGroup{GroupName: lgroup.Name, LdapGroupDN: dn, GroupType: common.LDAPGroupType})
|
||||
}
|
||||
}
|
||||
userGroupsList[curIndex] = userGroups
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
log.Warningf("failed to verify and populate ldap group parallel, error %v", err)
|
||||
}
|
||||
ugs := make([]ugModel.UserGroup, 0)
|
||||
for _, userGroups := range userGroupsList {
|
||||
ugs = append(ugs, userGroups...)
|
||||
}
|
||||
|
||||
groupIDsList, err := ugCtl.Ctl.Populate(ctx, ugs)
|
||||
if err != nil {
|
||||
log.Warningf("Failed to populate user groups :%v", err)
|
||||
}
|
||||
u.GroupIDs = groupIDsList
|
||||
}
|
||||
|
||||
func verifyGroupInLDAP(groupDN string, sess *ldap.Session) (*model.Group, bool) {
|
||||
if _, err := goldap.ParseDN(groupDN); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
lGroups, err := sess.SearchGroupByDN(groupDN)
|
||||
if err != nil {
|
||||
log.Warningf("Can not get the ldap group name with DN %v, error %v", groupDN, err)
|
||||
return nil, false
|
||||
}
|
||||
if len(lGroups) == 0 {
|
||||
log.Warningf("Can not get the ldap group name with DN %v", groupDN)
|
||||
return nil, false
|
||||
}
|
||||
return &lGroups[0], true
|
||||
}
|
||||
|
||||
func (l *Auth) syncUserInfoFromDB(ctx context.Context, u *models.User) {
|
||||
// Retrieve SysAdminFlag from DB so that it transfer to session
|
||||
dbUser, err := l.userMgr.GetByName(ctx, u.Username)
|
||||
|
25
src/go.mod
25
src/go.mod
@ -21,19 +21,19 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.7
|
||||
github.com/go-ldap/ldap/v3 v3.4.6
|
||||
github.com/go-openapi/errors v0.22.0
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/loads v0.21.2
|
||||
github.com/go-openapi/runtime v0.26.2
|
||||
github.com/go-openapi/spec v0.20.11 // indirect
|
||||
github.com/go-openapi/spec v0.20.11
|
||||
github.com/go-openapi/strfmt v0.23.0
|
||||
github.com/go-openapi/swag v0.23.0
|
||||
github.com/go-openapi/validate v0.22.3 // indirect
|
||||
github.com/go-openapi/validate v0.22.3
|
||||
github.com/go-redis/redis/v8 v8.11.4
|
||||
github.com/gocarina/gocsv v0.0.0-20210516172204-ca9e8a8ddea8
|
||||
github.com/gocraft/work v0.5.1
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/golang-migrate/migrate/v4 v4.17.1
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/google/go-containerregistry v0.19.2
|
||||
github.com/google/go-containerregistry v0.20.2
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/csrf v1.7.2
|
||||
github.com/gorilla/handlers v1.5.2
|
||||
@ -63,7 +63,7 @@ require (
|
||||
go.opentelemetry.io/otel/sdk v1.27.0
|
||||
go.opentelemetry.io/otel/trace v1.28.0
|
||||
go.uber.org/ratelimit v0.3.1
|
||||
golang.org/x/crypto v0.24.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.21.0
|
||||
golang.org/x/sync v0.7.0
|
||||
@ -71,10 +71,10 @@ require (
|
||||
golang.org/x/time v0.5.0
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
helm.sh/helm/v3 v3.15.2
|
||||
k8s.io/api v0.30.2
|
||||
k8s.io/apimachinery v0.30.2
|
||||
k8s.io/client-go v0.30.0
|
||||
helm.sh/helm/v3 v3.15.4
|
||||
k8s.io/api v0.30.3
|
||||
k8s.io/apimachinery v0.30.3
|
||||
k8s.io/client-go v0.30.3
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
)
|
||||
|
||||
@ -100,8 +100,7 @@ require (
|
||||
github.com/denverdino/aliyungo v0.0.0-20191227032621-df38c6fa730c // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dnaeon/go-vcr v1.2.0 // indirect
|
||||
github.com/docker/cli v25.0.1+incompatible // indirect
|
||||
github.com/docker/docker v25.0.5+incompatible // indirect
|
||||
github.com/docker/cli v27.1.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
@ -173,8 +172,8 @@ require (
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/term v0.22.0 // indirect
|
||||
google.golang.org/api v0.171.0 // indirect
|
||||
google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240520151616-dc85e6b867a5 // indirect
|
||||
|
44
src/go.sum
44
src/go.sum
@ -117,10 +117,10 @@ github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK
|
||||
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU=
|
||||
github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
|
||||
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
||||
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg=
|
||||
github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
@ -250,8 +250,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-containerregistry v0.19.2 h1:TannFKE1QSajsP6hPWb5oJNgKe1IKjHukIKDUmvsV6w=
|
||||
github.com/google/go-containerregistry v0.19.2/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
|
||||
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
|
||||
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@ -627,8 +627,8 @@ 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.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
@ -668,8 +668,6 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
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.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -716,16 +714,16 @@ 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.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
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.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -783,8 +781,6 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291/go.
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
|
||||
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
@ -834,17 +830,17 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
helm.sh/helm/v3 v3.15.2 h1:/3XINUFinJOBjQplGnjw92eLGpgXXp1L8chWPkCkDuw=
|
||||
helm.sh/helm/v3 v3.15.2/go.mod h1:FzSIP8jDQaa6WAVg9F+OkKz7J0ZmAga4MABtTbsb9WQ=
|
||||
helm.sh/helm/v3 v3.15.4 h1:UFHd6oZ1IN3FsUZ7XNhOQDyQ2QYknBNWRHH57e9cbHY=
|
||||
helm.sh/helm/v3 v3.15.4/go.mod h1:phOwlxqGSgppCY/ysWBNRhG3MtnpsttOzxaTK+Mt40E=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/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.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI=
|
||||
k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI=
|
||||
k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg=
|
||||
k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
|
||||
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
|
||||
k8s.io/api v0.30.3 h1:ImHwK9DCsPA9uoU3rVh4QHAHHK5dTSv1nxJUapx8hoQ=
|
||||
k8s.io/api v0.30.3/go.mod h1:GPc8jlzoe5JG3pb0KJCSLX5oAFIW3/qNJITlDj8BH04=
|
||||
k8s.io/apimachinery v0.30.3 h1:q1laaWCmrszyQuSQCfNB8cFgCuDAoPszKY4ucAjDwHc=
|
||||
k8s.io/apimachinery v0.30.3/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
|
||||
k8s.io/client-go v0.30.3 h1:bHrJu3xQZNXIi8/MoxYtZBBWQQXwy16zqJwloXXfD3k=
|
||||
k8s.io/client-go v0.30.3/go.mod h1:8d4pf8vYu665/kUbsxWAQ/JDBNWqfFeZnvFiVdmx89U=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
|
@ -318,18 +318,16 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
return errGcStop
|
||||
}
|
||||
|
||||
atomic.AddInt64(&index, 1)
|
||||
index := atomic.LoadInt64(&index)
|
||||
|
||||
localIndex := atomic.AddInt64(&index, 1)
|
||||
// set the status firstly, if the blob is updated by any HEAD/PUT request, it should be fail and skip.
|
||||
blob.Status = blobModels.StatusDeleting
|
||||
count, err := gc.blobMgr.UpdateBlobStatus(ctx.SystemContext(), blob)
|
||||
if err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to mark gc candidate deleting, skip: %s, %s", uid, index, total, blob.Digest, blob.Status)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to mark gc candidate deleting, skip: %s, %s", uid, localIndex, total, blob.Digest, blob.Status)
|
||||
continue
|
||||
}
|
||||
if count == 0 {
|
||||
gc.logger.Warningf("[%s][%d/%d] no blob found to mark gc candidate deleting, ID:%d, digest:%s", uid, index, total, blob.ID, blob.Digest)
|
||||
gc.logger.Warningf("[%s][%d/%d] no blob found to mark gc candidate deleting, ID:%d, digest:%s", uid, localIndex, total, blob.ID, blob.Digest)
|
||||
continue
|
||||
}
|
||||
|
||||
@ -339,7 +337,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
for _, art := range gc.trashedArts[blob.Digest] {
|
||||
// Harbor cannot know the existing tags in the backend from its database, so let the v2 DELETE manifest to remove all of them.
|
||||
gc.logger.Infof("[%s][%d/%d] delete the manifest with registry v2 API: %s, %s, %s",
|
||||
uid, index, total, art.RepositoryName, blob.ContentType, blob.Digest)
|
||||
uid, localIndex, total, art.RepositoryName, blob.ContentType, blob.Digest)
|
||||
if err := retry.Retry(func() error {
|
||||
return ignoreNotFound(func() error {
|
||||
err := v2DeleteManifest(art.RepositoryName, blob.Digest)
|
||||
@ -350,13 +348,13 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
return err
|
||||
})
|
||||
}, retry.Callback(func(err error, sleep time.Duration) {
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec v2DeleteManifest, error: %v, will retry again after: %s", uid, index, total, err, sleep)
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec v2DeleteManifest, error: %v, will retry again after: %s", uid, localIndex, total, err, sleep)
|
||||
})); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete manifest with v2 API, %s, %s, %v", uid, index, total, art.RepositoryName, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete manifest with v2 API, %s, %s, %v", uid, localIndex, total, art.RepositoryName, blob.Digest, err)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.markDeleteFailed(ctx, blob)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after v2DeleteManifest() error out: %s, %v", uid, index, total, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after v2DeleteManifest() error out: %s, %v", uid, localIndex, total, blob.Digest, err)
|
||||
return err
|
||||
}
|
||||
// if the system is set to read-only mode, return directly
|
||||
@ -367,7 +365,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
continue
|
||||
}
|
||||
// for manifest, it has to delete the revisions folder of each repository
|
||||
gc.logger.Infof("[%s][%d/%d] delete manifest from storage: %s", uid, index, total, blob.Digest)
|
||||
gc.logger.Infof("[%s][%d/%d] delete manifest from storage: %s", uid, localIndex, total, blob.Digest)
|
||||
if err := retry.Retry(func() error {
|
||||
return ignoreNotFound(func() error {
|
||||
err := gc.registryCtlClient.DeleteManifest(art.RepositoryName, blob.Digest)
|
||||
@ -378,13 +376,13 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
return err
|
||||
})
|
||||
}, retry.Callback(func(err error, sleep time.Duration) {
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec DeleteManifest, error: %v, will retry again after: %s", uid, index, total, err, sleep)
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec DeleteManifest, error: %v, will retry again after: %s", uid, localIndex, total, err, sleep)
|
||||
})); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to remove manifest from storage: %s, %s, errMsg=%v", uid, index, total, art.RepositoryName, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to remove manifest from storage: %s, %s, errMsg=%v", uid, localIndex, total, art.RepositoryName, blob.Digest, err)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.markDeleteFailed(ctx, blob)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.registryCtlClient.DeleteManifest() error out: %s, %s, %v", uid, index, total, art.RepositoryName, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.registryCtlClient.DeleteManifest() error out: %s, %s, %v", uid, localIndex, total, art.RepositoryName, blob.Digest, err)
|
||||
return err
|
||||
}
|
||||
// if the system is set to read-only mode, return directly
|
||||
@ -395,19 +393,19 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
continue
|
||||
}
|
||||
|
||||
gc.logger.Infof("[%s][%d/%d] delete artifact blob record from database: %d, %s, %s", uid, index, total, art.ID, art.RepositoryName, art.Digest)
|
||||
gc.logger.Infof("[%s][%d/%d] delete artifact blob record from database: %d, %s, %s", uid, localIndex, total, art.ID, art.RepositoryName, art.Digest)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.blobMgr.CleanupAssociationsForArtifact(ctx.SystemContext(), art.Digest)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.blobMgr.CleanupAssociationsForArtifact(): %v, errMsg=%v", uid, index, total, art.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.blobMgr.CleanupAssociationsForArtifact(): %v, errMsg=%v", uid, localIndex, total, art.Digest, err)
|
||||
return err
|
||||
}
|
||||
|
||||
gc.logger.Infof("[%s][%d/%d] delete artifact trash record from database: %d, %s, %s", uid, index, total, art.ID, art.RepositoryName, art.Digest)
|
||||
gc.logger.Infof("[%s][%d/%d] delete artifact trash record from database: %d, %s, %s", uid, localIndex, total, art.ID, art.RepositoryName, art.Digest)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.artrashMgr.Delete(ctx.SystemContext(), art.ID)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.artrashMgr.Delete(): %v, errMsg=%v", uid, index, total, art.ID, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.artrashMgr.Delete(): %v, errMsg=%v", uid, localIndex, total, art.ID, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -421,7 +419,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
// delete all the blobs, which include config, layer and manifest
|
||||
// for the foreign layer, as it's not stored in the storage, no need to call the delete api and count size, but still have to delete the DB record.
|
||||
if !blob.IsForeignLayer() {
|
||||
gc.logger.Infof("[%s][%d/%d] delete blob from storage: %s", uid, index, total, blob.Digest)
|
||||
gc.logger.Infof("[%s][%d/%d] delete blob from storage: %s", uid, localIndex, total, blob.Digest)
|
||||
if err := retry.Retry(func() error {
|
||||
return ignoreNotFound(func() error {
|
||||
err := gc.registryCtlClient.DeleteBlob(blob.Digest)
|
||||
@ -432,13 +430,13 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
return err
|
||||
})
|
||||
}, retry.Callback(func(err error, sleep time.Duration) {
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec DeleteBlob, error: %v, will retry again after: %s", uid, index, total, err, sleep)
|
||||
gc.logger.Infof("[%s][%d/%d] failed to exec DeleteBlob, error: %v, will retry again after: %s", uid, localIndex, total, err, sleep)
|
||||
})); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete blob from storage: %s, %s, errMsg=%v", uid, index, total, blob.Digest, blob.Status, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete blob from storage: %s, %s, errMsg=%v", uid, localIndex, total, blob.Digest, blob.Status, err)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.markDeleteFailed(ctx, blob)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.registryCtlClient.DeleteBlob() error out: %s, %v", uid, index, total, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.registryCtlClient.DeleteBlob() error out: %s, %v", uid, localIndex, total, blob.Digest, err)
|
||||
return err
|
||||
}
|
||||
// if the system is set to read-only mode, return directly
|
||||
@ -450,15 +448,15 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
||||
atomic.AddInt64(&sweepSize, blob.Size)
|
||||
}
|
||||
|
||||
gc.logger.Infof("[%s][%d/%d] delete blob record from database: %d, %s", uid, index, total, blob.ID, blob.Digest)
|
||||
gc.logger.Infof("[%s][%d/%d] delete blob record from database: %d, %s", uid, localIndex, total, blob.ID, blob.Digest)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.blobMgr.Delete(ctx.SystemContext(), blob.ID)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete blob from database: %s, %s, errMsg=%v", uid, index, total, blob.Digest, blob.Status, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to delete blob from database: %s, %s, errMsg=%v", uid, localIndex, total, blob.Digest, blob.Status, err)
|
||||
if err := ignoreNotFound(func() error {
|
||||
return gc.markDeleteFailed(ctx, blob)
|
||||
}); err != nil {
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.blobMgr.Delete() error out, %d, %s %v", uid, index, total, blob.ID, blob.Digest, err)
|
||||
gc.logger.Errorf("[%s][%d/%d] failed to call gc.markDeleteFailed() after gc.blobMgr.Delete() error out, %d, %s %v", uid, localIndex, total, blob.ID, blob.Digest, err)
|
||||
return err
|
||||
}
|
||||
return err
|
||||
|
@ -42,8 +42,8 @@ import (
|
||||
type gcTestSuite struct {
|
||||
htesting.Suite
|
||||
artifactCtl *artifacttesting.Controller
|
||||
artrashMgr *trashtesting.FakeManager
|
||||
registryCtlClient *registryctl.Mockclient
|
||||
artrashMgr *trashtesting.Manager
|
||||
registryCtlClient *registryctl.Client
|
||||
projectCtl *projecttesting.Controller
|
||||
blobMgr *blob.Manager
|
||||
|
||||
@ -54,8 +54,8 @@ type gcTestSuite struct {
|
||||
|
||||
func (suite *gcTestSuite) SetupTest() {
|
||||
suite.artifactCtl = &artifacttesting.Controller{}
|
||||
suite.artrashMgr = &trashtesting.FakeManager{}
|
||||
suite.registryCtlClient = ®istryctl.Mockclient{}
|
||||
suite.artrashMgr = &trashtesting.Manager{}
|
||||
suite.registryCtlClient = ®istryctl.Client{}
|
||||
suite.blobMgr = &blob.Manager{}
|
||||
suite.projectCtl = &projecttesting.Controller{}
|
||||
|
||||
@ -98,7 +98,7 @@ func (suite *gcTestSuite) TestDeletedArt() {
|
||||
},
|
||||
}, nil)
|
||||
suite.artifactCtl.On("Delete").Return(nil)
|
||||
suite.artrashMgr.On("Filter").Return([]model.ArtifactTrash{
|
||||
mock.OnAnything(suite.artrashMgr, "Filter").Return([]model.ArtifactTrash{
|
||||
{
|
||||
ID: 1,
|
||||
Digest: suite.DigestString(),
|
||||
@ -163,6 +163,8 @@ func (suite *gcTestSuite) TestInit() {
|
||||
"time_window": 1,
|
||||
"workers": float64(3),
|
||||
}
|
||||
|
||||
mock.OnAnything(gc.registryCtlClient, "Health").Return(nil)
|
||||
suite.Nil(gc.init(ctx, params))
|
||||
suite.True(gc.deleteUntagged)
|
||||
suite.Equal(3, gc.workers)
|
||||
@ -230,7 +232,7 @@ func (suite *gcTestSuite) TestRun() {
|
||||
},
|
||||
}, nil)
|
||||
suite.artifactCtl.On("Delete").Return(nil)
|
||||
suite.artrashMgr.On("Filter").Return([]model.ArtifactTrash{}, nil)
|
||||
mock.OnAnything(suite.artrashMgr, "Filter").Return([]model.ArtifactTrash{}, nil)
|
||||
|
||||
mock.OnAnything(suite.projectCtl, "List").Return([]*proModels.Project{
|
||||
{
|
||||
@ -271,6 +273,8 @@ func (suite *gcTestSuite) TestRun() {
|
||||
|
||||
mock.OnAnything(suite.blobMgr, "Delete").Return(nil)
|
||||
|
||||
mock.OnAnything(suite.registryCtlClient, "Health").Return(nil)
|
||||
|
||||
gc := &GarbageCollector{
|
||||
artCtl: suite.artifactCtl,
|
||||
artrashMgr: suite.artrashMgr,
|
||||
@ -284,6 +288,7 @@ func (suite *gcTestSuite) TestRun() {
|
||||
"workers": 3,
|
||||
}
|
||||
|
||||
mock.OnAnything(gc.registryCtlClient, "DeleteBlob").Return(nil)
|
||||
suite.Nil(gc.Run(ctx, params))
|
||||
}
|
||||
|
||||
@ -302,7 +307,7 @@ func (suite *gcTestSuite) TestMark() {
|
||||
},
|
||||
}, nil)
|
||||
suite.artifactCtl.On("Delete").Return(nil)
|
||||
suite.artrashMgr.On("Filter").Return([]model.ArtifactTrash{
|
||||
mock.OnAnything(suite.artrashMgr, "Filter").Return([]model.ArtifactTrash{
|
||||
{
|
||||
ID: 1,
|
||||
Digest: suite.DigestString(),
|
||||
@ -381,6 +386,7 @@ func (suite *gcTestSuite) TestSweep() {
|
||||
workers: 3,
|
||||
}
|
||||
|
||||
mock.OnAnything(gc.registryCtlClient, "DeleteBlob").Return(nil)
|
||||
suite.Nil(gc.sweep(ctx))
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ var (
|
||||
{Name: common.LDAPURL, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_URL", DefaultValue: "", ItemType: &NonEmptyStringType{}, Editable: false, Description: `The URL of LDAP server`},
|
||||
{Name: common.LDAPVerifyCert, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_VERIFY_CERT", DefaultValue: "true", ItemType: &BoolType{}, Editable: false, Description: `Whether verify your OIDC server certificate, disable it if your OIDC server is hosted via self-hosted certificate.`},
|
||||
{Name: common.LDAPGroupMembershipAttribute, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_GROUP_MEMBERSHIP_ATTRIBUTE", DefaultValue: "memberof", ItemType: &StringType{}, Editable: true, Description: `The user attribute to identify the group membership`},
|
||||
{Name: common.LDAPGroupAttachParallel, Scope: UserScope, Group: LdapBasicGroup, EnvKey: "LDAP_GROUP_ATTACH_PARALLEL", DefaultValue: "false", ItemType: &BoolType{}, Editable: true, Description: `Attach LDAP group information to Harbor in parallel`},
|
||||
|
||||
{Name: common.MaxJobWorkers, Scope: SystemScope, Group: BasicGroup, EnvKey: "MAX_JOB_WORKERS", DefaultValue: "10", ItemType: &IntType{}, Editable: false},
|
||||
{Name: common.ScanAllPolicy, Scope: UserScope, Group: BasicGroup, EnvKey: "", DefaultValue: "", ItemType: &MapType{}, Editable: false, Description: `The policy to scan images`},
|
||||
|
@ -94,6 +94,7 @@ type GroupConf struct {
|
||||
SearchScope int `json:"ldap_group_search_scope"`
|
||||
AdminDN string `json:"ldap_group_admin_dn,omitempty"`
|
||||
MembershipAttribute string `json:"ldap_group_membership_attribute,omitempty"`
|
||||
AttachParallel bool `json:"ldap_group_attach_parallel,omitempty"`
|
||||
}
|
||||
|
||||
type GDPRSetting struct {
|
||||
|
@ -81,6 +81,7 @@ func LDAPGroupConf(ctx context.Context) (*cfgModels.GroupConf, error) {
|
||||
SearchScope: mgr.Get(ctx, common.LDAPGroupSearchScope).GetInt(),
|
||||
AdminDN: mgr.Get(ctx, common.LDAPGroupAdminDn).GetString(),
|
||||
MembershipAttribute: mgr.Get(ctx, common.LDAPGroupMembershipAttribute).GetString(),
|
||||
AttachParallel: mgr.Get(ctx, common.LDAPGroupAttachParallel).GetBool(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package transfer
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -54,6 +54,8 @@ type DAO interface {
|
||||
DeleteReference(ctx context.Context, id int64) (err error)
|
||||
// DeleteReferences deletes the references referenced by the artifact specified by parent ID
|
||||
DeleteReferences(ctx context.Context, parentID int64) (err error)
|
||||
// ListWithLatest ...
|
||||
ListWithLatest(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
|
||||
}
|
||||
|
||||
const (
|
||||
@ -282,6 +284,53 @@ func (d *dao) DeleteReferences(ctx context.Context, parentID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *dao) ListWithLatest(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error) {
|
||||
ormer, err := orm.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sql := `SELECT a.*
|
||||
FROM artifact a
|
||||
JOIN (
|
||||
SELECT repository_name, MAX(push_time) AS latest_push_time
|
||||
FROM artifact
|
||||
WHERE project_id = ? and %s = ?
|
||||
GROUP BY repository_name
|
||||
) latest ON a.repository_name = latest.repository_name AND a.push_time = latest.latest_push_time`
|
||||
|
||||
queryParam := make([]interface{}, 0)
|
||||
var ok bool
|
||||
var pid interface{}
|
||||
if pid, ok = query.Keywords["ProjectID"]; !ok {
|
||||
return nil, errors.New(nil).WithCode(errors.BadRequestCode).
|
||||
WithMessage(`the value of "ProjectID" must be set`)
|
||||
}
|
||||
queryParam = append(queryParam, pid)
|
||||
|
||||
var attributionValue interface{}
|
||||
if attributionValue, ok = query.Keywords["media_type"]; ok {
|
||||
sql = fmt.Sprintf(sql, "media_type")
|
||||
} else if attributionValue, ok = query.Keywords["artifact_type"]; ok {
|
||||
sql = fmt.Sprintf(sql, "artifact_type")
|
||||
}
|
||||
|
||||
if attributionValue == "" {
|
||||
return nil, errors.New(nil).WithCode(errors.BadRequestCode).
|
||||
WithMessage(`the value of "media_type" or "artifact_type" must be set`)
|
||||
}
|
||||
queryParam = append(queryParam, attributionValue)
|
||||
|
||||
sql, queryParam = orm.PaginationOnRawSQL(query, sql, queryParam)
|
||||
arts := []*Artifact{}
|
||||
_, err = ormer.Raw(sql, queryParam...).QueryRows(&arts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return arts, nil
|
||||
}
|
||||
|
||||
func querySetter(ctx context.Context, query *q.Query, options ...orm.Option) (beegoorm.QuerySeter, error) {
|
||||
qs, err := orm.QuerySetter(ctx, &Artifact{}, query, options...)
|
||||
if err != nil {
|
||||
|
@ -472,6 +472,75 @@ func (d *daoTestSuite) TestDeleteReferences() {
|
||||
d.True(errors.IsErr(err, errors.NotFoundCode))
|
||||
}
|
||||
|
||||
func (d *daoTestSuite) TestListWithLatest() {
|
||||
now := time.Now()
|
||||
art := &Artifact{
|
||||
Type: "IMAGE",
|
||||
MediaType: v1.MediaTypeImageConfig,
|
||||
ManifestMediaType: v1.MediaTypeImageIndex,
|
||||
ProjectID: 1234,
|
||||
RepositoryID: 1234,
|
||||
RepositoryName: "library2/hello-world1",
|
||||
Digest: "digest",
|
||||
PushTime: now,
|
||||
PullTime: now,
|
||||
Annotations: `{"anno1":"value1"}`,
|
||||
}
|
||||
id, err := d.dao.Create(d.ctx, art)
|
||||
d.Require().Nil(err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
now = time.Now()
|
||||
|
||||
art2 := &Artifact{
|
||||
Type: "IMAGE",
|
||||
MediaType: v1.MediaTypeImageConfig,
|
||||
ManifestMediaType: v1.MediaTypeImageIndex,
|
||||
ProjectID: 1234,
|
||||
RepositoryID: 1235,
|
||||
RepositoryName: "library2/hello-world2",
|
||||
Digest: "digest",
|
||||
PushTime: now,
|
||||
PullTime: now,
|
||||
Annotations: `{"anno1":"value1"}`,
|
||||
}
|
||||
id1, err := d.dao.Create(d.ctx, art2)
|
||||
d.Require().Nil(err)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
now = time.Now()
|
||||
|
||||
art3 := &Artifact{
|
||||
Type: "IMAGE",
|
||||
MediaType: v1.MediaTypeImageConfig,
|
||||
ManifestMediaType: v1.MediaTypeImageIndex,
|
||||
ProjectID: 1234,
|
||||
RepositoryID: 1235,
|
||||
RepositoryName: "library2/hello-world2",
|
||||
Digest: "digest2",
|
||||
PushTime: now,
|
||||
PullTime: now,
|
||||
Annotations: `{"anno1":"value1"}`,
|
||||
}
|
||||
id2, err := d.dao.Create(d.ctx, art3)
|
||||
d.Require().Nil(err)
|
||||
|
||||
latest, err := d.dao.ListWithLatest(d.ctx, &q.Query{
|
||||
Keywords: map[string]interface{}{
|
||||
"ProjectID": 1234,
|
||||
"media_type": v1.MediaTypeImageConfig,
|
||||
},
|
||||
})
|
||||
|
||||
d.Require().Nil(err)
|
||||
d.Require().Equal(2, len(latest))
|
||||
d.Equal("library2/hello-world1", latest[0].RepositoryName)
|
||||
|
||||
defer d.dao.Delete(d.ctx, id)
|
||||
defer d.dao.Delete(d.ctx, id1)
|
||||
defer d.dao.Delete(d.ctx, id2)
|
||||
}
|
||||
|
||||
func TestDaoTestSuite(t *testing.T) {
|
||||
suite.Run(t, &daoTestSuite{})
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ type Manager interface {
|
||||
ListReferences(ctx context.Context, query *q.Query) (references []*Reference, err error)
|
||||
// DeleteReference specified by ID
|
||||
DeleteReference(ctx context.Context, id int64) (err error)
|
||||
// ListWithLatest list the artifacts when the latest_in_repository in the query was set
|
||||
ListWithLatest(ctx context.Context, query *q.Query) (artifacts []*Artifact, err error)
|
||||
}
|
||||
|
||||
// NewManager returns an instance of the default manager
|
||||
@ -147,6 +149,22 @@ func (m *manager) DeleteReference(ctx context.Context, id int64) error {
|
||||
return m.dao.DeleteReference(ctx, id)
|
||||
}
|
||||
|
||||
func (m *manager) ListWithLatest(ctx context.Context, query *q.Query) ([]*Artifact, error) {
|
||||
arts, err := m.dao.ListWithLatest(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var artifacts []*Artifact
|
||||
for _, art := range arts {
|
||||
artifact, err := m.assemble(ctx, art)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
artifacts = append(artifacts, artifact)
|
||||
}
|
||||
return artifacts, nil
|
||||
}
|
||||
|
||||
// assemble the artifact with references populated
|
||||
func (m *manager) assemble(ctx context.Context, art *dao.Artifact) (*Artifact, error) {
|
||||
artifact := &Artifact{}
|
||||
|
@ -80,6 +80,11 @@ func (f *fakeDao) DeleteReferences(ctx context.Context, parentID int64) error {
|
||||
return args.Error(0)
|
||||
}
|
||||
|
||||
func (f *fakeDao) ListWithLatest(ctx context.Context, query *q.Query) ([]*dao.Artifact, error) {
|
||||
args := f.Called()
|
||||
return args.Get(0).([]*dao.Artifact), args.Error(1)
|
||||
}
|
||||
|
||||
type managerTestSuite struct {
|
||||
suite.Suite
|
||||
mgr *manager
|
||||
@ -135,6 +140,28 @@ func (m *managerTestSuite) TestAssemble() {
|
||||
m.Equal(2, len(artifact.References))
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestListWithLatest() {
|
||||
art := &dao.Artifact{
|
||||
ID: 1,
|
||||
Type: "IMAGE",
|
||||
MediaType: "application/vnd.oci.image.config.v1+json",
|
||||
ManifestMediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
ProjectID: 1,
|
||||
RepositoryID: 1,
|
||||
Digest: "sha256:418fb88ec412e340cdbef913b8ca1bbe8f9e8dc705f9617414c1f2c8db980180",
|
||||
Size: 1024,
|
||||
PushTime: time.Now(),
|
||||
PullTime: time.Now(),
|
||||
ExtraAttrs: `{"attr1":"value1"}`,
|
||||
Annotations: `{"anno1":"value1"}`,
|
||||
}
|
||||
m.dao.On("ListWithLatest", mock.Anything).Return([]*dao.Artifact{art}, nil)
|
||||
artifacts, err := m.mgr.ListWithLatest(nil, nil)
|
||||
m.Require().Nil(err)
|
||||
m.Equal(1, len(artifacts))
|
||||
m.Equal(art.ID, artifacts[0].ID)
|
||||
}
|
||||
|
||||
func (m *managerTestSuite) TestList() {
|
||||
art := &dao.Artifact{
|
||||
ID: 1,
|
||||
|
@ -65,6 +65,10 @@ func (m *Manager) List(ctx context.Context, query *q.Query) ([]*artifact.Artifac
|
||||
return m.delegator.List(ctx, query)
|
||||
}
|
||||
|
||||
func (m *Manager) ListWithLatest(ctx context.Context, query *q.Query) ([]*artifact.Artifact, error) {
|
||||
return m.delegator.ListWithLatest(ctx, query)
|
||||
}
|
||||
|
||||
func (m *Manager) Create(ctx context.Context, artifact *artifact.Artifact) (int64, error) {
|
||||
return m.delegator.Create(ctx, artifact)
|
||||
}
|
||||
|
@ -24,4 +24,5 @@ const (
|
||||
ProMetaAutoScan = "auto_scan"
|
||||
ProMetaReuseSysCVEAllowlist = "reuse_sys_cve_allowlist"
|
||||
ProMetaAutoSBOMGen = "auto_sbom_generation"
|
||||
ProMetaProxySpeed = "proxy_speed_kb"
|
||||
)
|
||||
|
@ -156,6 +156,19 @@ func (p *Project) AutoSBOMGen() bool {
|
||||
return isTrue(auto)
|
||||
}
|
||||
|
||||
// ProxyCacheSpeed ...
|
||||
func (p *Project) ProxyCacheSpeed() int32 {
|
||||
speed, exist := p.GetMetadata(ProMetaProxySpeed)
|
||||
if !exist {
|
||||
return 0
|
||||
}
|
||||
speedInt, err := strconv.ParseInt(speed, 10, 32)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return int32(speedInt)
|
||||
}
|
||||
|
||||
// FilterByPublic returns orm.QuerySeter with public filter
|
||||
func (p *Project) FilterByPublic(_ context.Context, qs orm.QuerySeter, _ string, value interface{}) orm.QuerySeter {
|
||||
subQuery := `SELECT project_id FROM project_metadata WHERE name = 'public' AND value = '%s'`
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ecrPattern = "https://(?:api|(\\d+)\\.dkr)\\.ecr\\.([\\w\\-]+)\\.amazonaws\\.com"
|
||||
ecrPattern = "https://(?:api|(\\d+)\\.dkr)\\.ecr(\\-fips)?\\.([\\w\\-]+)\\.(amazonaws\\.com(\\.cn)?|sc2s\\.sgov\\.gov|c2s\\.ic\\.gov)"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -64,10 +64,10 @@ func newAdapter(registry *model.Registry) (*adapter, error) {
|
||||
|
||||
func parseAccountRegion(url string) (string, string, error) {
|
||||
rs := ecrRegexp.FindStringSubmatch(url)
|
||||
if rs == nil {
|
||||
if rs == nil || len(rs) < 4 {
|
||||
return "", "", errors.New("bad aws url")
|
||||
}
|
||||
return rs[1], rs[2], nil
|
||||
return rs[1], rs[3], nil
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
|
@ -83,6 +83,38 @@ func TestAdapter_NewAdapter(t *testing.T) {
|
||||
assert.Nil(t, adapter)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
adapter, err = newAdapter(&model.Registry{
|
||||
Type: model.RegistryTypeAwsEcr,
|
||||
Credential: &model.Credential{
|
||||
AccessKey: "xxx",
|
||||
AccessSecret: "ppp",
|
||||
},
|
||||
URL: "https://123456.dkr.ecr-fips.test-region.amazonaws.com",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, adapter)
|
||||
|
||||
adapter, err = newAdapter(&model.Registry{
|
||||
Type: model.RegistryTypeAwsEcr,
|
||||
Credential: &model.Credential{
|
||||
AccessKey: "xxx",
|
||||
AccessSecret: "ppp",
|
||||
},
|
||||
URL: "https://123456.dkr.ecr.us-isob-east-1.sc2s.sgov.gov",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, adapter)
|
||||
|
||||
adapter, err = newAdapter(&model.Registry{
|
||||
Type: model.RegistryTypeAwsEcr,
|
||||
Credential: &model.Credential{
|
||||
AccessKey: "xxx",
|
||||
AccessSecret: "ppp",
|
||||
},
|
||||
URL: "https://123456.dkr.ecr.us-iso-east-1.c2s.ic.gov",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, adapter)
|
||||
}
|
||||
|
||||
func getMockAdapter(t *testing.T, hasCred, health bool) (*adapter, *httptest.Server) {
|
||||
|
@ -52,6 +52,7 @@ func (suite *DaoTestSuite) robots() {
|
||||
Description: "test3 description",
|
||||
ProjectID: 1,
|
||||
Secret: suite.RandString(10),
|
||||
Creator: "tester",
|
||||
})
|
||||
suite.Nil(err)
|
||||
|
||||
@ -120,6 +121,7 @@ func (suite *DaoTestSuite) TestGet() {
|
||||
r, err := suite.dao.Get(orm.Context(), suite.robotID3)
|
||||
suite.Nil(err)
|
||||
suite.Equal("test3", r.Name)
|
||||
suite.Equal("tester", r.Creator)
|
||||
}
|
||||
|
||||
func (suite *DaoTestSuite) TestCount() {
|
||||
|
@ -39,6 +39,7 @@ type Robot struct {
|
||||
ExpiresAt int64 `orm:"column(expiresat)" json:"expires_at"`
|
||||
Disabled bool `orm:"column(disabled)" json:"disabled"`
|
||||
Visible bool `orm:"column(visible)" json:"-"`
|
||||
Creator string `orm:"column(creator)" json:"creator"`
|
||||
CreationTime time.Time `orm:"column(creation_time);auto_now_add" json:"creation_time"`
|
||||
UpdateTime time.Time `orm:"column(update_time);auto_now" json:"update_time"`
|
||||
}
|
||||
|
@ -62,12 +62,12 @@ func TestPostScan(t *testing.T) {
|
||||
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)
|
||||
mocker := &postprocessorstesting.NativeScanReportConverter{}
|
||||
mocker.On("ToRelationalSchema", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", "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.Equal(t, "original report", refreshedReport, "PostScan should return the refreshed report")
|
||||
assert.Nil(t, err, "PostScan should not return an error")
|
||||
}
|
||||
|
||||
@ -209,6 +209,7 @@ func (suite *VulHandlerTestSuite) TestMakeReportPlaceHolder() {
|
||||
mock.OnAnything(suite.reportMgr, "Create").Return("uuid", nil).Once()
|
||||
mock.OnAnything(suite.reportMgr, "Delete").Return(nil).Once()
|
||||
mock.OnAnything(suite.taskMgr, "ListScanTasksByReportUUID").Return([]*task.Task{{Status: "Success"}}, nil)
|
||||
mock.OnAnything(suite.handler.reportConverter, "FromRelationalSchema").Return("", nil)
|
||||
rps, err := suite.handler.MakePlaceHolder(ctx, art, r)
|
||||
require.NoError(suite.T(), err)
|
||||
assert.Equal(suite.T(), 1, len(rps))
|
||||
|
@ -498,6 +498,37 @@
|
||||
</option>
|
||||
</select>
|
||||
</clr-select-container>
|
||||
<clr-checkbox-container>
|
||||
<label for="ldapGroupAttachParallel">
|
||||
{{ 'CONFIG.LDAP.GROUP_ATTACH_PARALLEL' | translate }}
|
||||
<clr-tooltip>
|
||||
<clr-icon
|
||||
clrTooltipTrigger
|
||||
shape="info-circle"
|
||||
size="24"></clr-icon>
|
||||
<clr-tooltip-content
|
||||
*clrIfOpen
|
||||
clrPosition="top-right"
|
||||
clrSize="lg">
|
||||
<span>{{
|
||||
'CONFIG.LDAP.GROUP_ATTACH_PARALLEL_INFO' | translate
|
||||
}}</span>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</label>
|
||||
<clr-checkbox-wrapper>
|
||||
<input
|
||||
(ngModelChange)="setLdapGroupAttachParallelValue($event)"
|
||||
[disabled]="
|
||||
disabled(currentConfig.ldap_group_attach_parallel)
|
||||
"
|
||||
[ngModel]="currentConfig.ldap_group_attach_parallel.value"
|
||||
clrCheckbox
|
||||
id="ldapGroupAttachParallel"
|
||||
name="ldapGroupAttachParallel"
|
||||
type="checkbox" />
|
||||
</clr-checkbox-wrapper>
|
||||
</clr-checkbox-container>
|
||||
</section>
|
||||
<clr-checkbox-container *ngIf="showSelfReg">
|
||||
<label for="selfReg"
|
||||
|
@ -11,15 +11,15 @@
|
||||
// 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.
|
||||
import { Component, ViewChild, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { AppConfigService } from '../../../../services/app-config.service';
|
||||
import { ConfigurationService } from '../../../../services/config.service';
|
||||
import { SystemInfoService } from '../../../../shared/services';
|
||||
import {
|
||||
isEmpty,
|
||||
getChanges as getChangesFunc,
|
||||
isEmpty,
|
||||
} from '../../../../shared/units/utils';
|
||||
import { CONFIG_AUTH_MODE } from '../../../../shared/entities/shared.const';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
@ -132,6 +132,9 @@ export class ConfigurationAuthComponent implements OnInit {
|
||||
this.currentConfig.ldap_verify_cert.value = $event;
|
||||
}
|
||||
|
||||
setLdapGroupAttachParallelValue($event: any) {
|
||||
this.currentConfig.ldap_group_attach_parallel.value = $event;
|
||||
}
|
||||
public pingTestServer(): void {
|
||||
if (this.testingOnGoing) {
|
||||
return; // Should not come here
|
||||
|
@ -72,6 +72,7 @@ export class Configuration {
|
||||
ldap_group_search_scope: NumberValueItem;
|
||||
ldap_group_membership_attribute: StringValueItem;
|
||||
ldap_group_admin_dn: StringValueItem;
|
||||
ldap_group_attach_parallel: BoolValueItem;
|
||||
uaa_client_id: StringValueItem;
|
||||
uaa_client_secret?: StringValueItem;
|
||||
uaa_endpoint: StringValueItem;
|
||||
|
@ -139,6 +139,7 @@
|
||||
}}</label>
|
||||
<app-label-selector
|
||||
[usedInDropdown]="true"
|
||||
[dropdownOpened]="true"
|
||||
[width]="200"
|
||||
[ownedLabels]="
|
||||
selectedRow[0]?.labels
|
||||
|
@ -928,7 +928,9 @@
|
||||
"LDAP_GROUP_MEMBERSHIP": "LDAP Group Membership",
|
||||
"LDAP_GROUP_MEMBERSHIP_INFO": "The attribute indicates the membership of LDAP group, default value is memberof, in some LDAP server it could be \"ismemberof\". This field cannot be empty if you need to enable the LDAP group related feature.",
|
||||
"GROUP_SCOPE": "LDAP Group Search Scope",
|
||||
"GROUP_SCOPE_INFO": "The scope to search for groups, select Subtree by default."
|
||||
"GROUP_SCOPE_INFO": "The scope to search for groups, select Subtree by default.",
|
||||
"GROUP_ATTACH_PARALLEL": "LDAP Group Attached In Parallel",
|
||||
"GROUP_ATTACH_PARALLEL_INFO": "Enable this option to attach group in parallel to avoid timeout when there are too many groups. If disabled, the LDAP group information will be attached sequentially."
|
||||
|
||||
},
|
||||
"UAA": {
|
||||
|
@ -926,7 +926,10 @@
|
||||
"LDAP_GROUP_MEMBERSHIP": "LDAP 组成员",
|
||||
"LDAP_GROUP_MEMBERSHIP_INFO": "LDAP组成员的membership属性,默认为 memberof, 在某些LDAP服务器会变为 ismemberof。如果要开启LDAP组功能,则此项必填",
|
||||
"GROUP_SCOPE": "LDAP组搜索范围",
|
||||
"GROUP_SCOPE_INFO": "搜索组的范围,默认值为\"子树\""
|
||||
"GROUP_SCOPE_INFO": "搜索组的范围,默认值为\"子树\"",
|
||||
"GROUP_ATTACH_PARALLEL": "LDAP组并行同步",
|
||||
"GROUP_ATTACH_PARALLEL_INFO": "打开这个选项时,LDAP组的信息是并行同步到Harbor, 这样可以防止用户组太多时造成的登录超时,如果关闭这个选项,LDAP组信息是顺序同步到Harbor"
|
||||
|
||||
},
|
||||
"UAA": {
|
||||
"ENDPOINT": "UAA Endpoint",
|
||||
|
@ -60,7 +60,7 @@ func BlobGetMiddleware() func(http.Handler) http.Handler {
|
||||
|
||||
func handleBlob(w http.ResponseWriter, r *http.Request, next http.Handler) error {
|
||||
ctx := r.Context()
|
||||
art, p, proxyCtl, err := preCheck(ctx)
|
||||
art, p, proxyCtl, err := preCheck(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -96,14 +96,14 @@ func handleBlob(w http.ResponseWriter, r *http.Request, next http.Handler) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func preCheck(ctx context.Context) (art lib.ArtifactInfo, p *proModels.Project, ctl proxy.Controller, err error) {
|
||||
func preCheck(ctx context.Context, withProjectMetadata bool) (art lib.ArtifactInfo, p *proModels.Project, ctl proxy.Controller, err error) {
|
||||
none := lib.ArtifactInfo{}
|
||||
art = lib.GetArtifactInfo(ctx)
|
||||
if art == none {
|
||||
return none, nil, nil, errors.New("artifactinfo is not found").WithCode(errors.NotFoundCode)
|
||||
}
|
||||
ctl = proxy.ControllerInstance()
|
||||
p, err = project.Ctl.GetByName(ctx, art.ProjectName, project.Metadata(false))
|
||||
p, err = project.Ctl.GetByName(ctx, art.ProjectName, project.Metadata(withProjectMetadata))
|
||||
return
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ func defaultBlobURL(projectName string, name string, digest string) string {
|
||||
|
||||
func handleManifest(w http.ResponseWriter, r *http.Request, next http.Handler) error {
|
||||
ctx := r.Context()
|
||||
art, p, proxyCtl, err := preCheck(ctx)
|
||||
art, p, proxyCtl, err := preCheck(ctx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -174,7 +174,7 @@ func handleManifest(w http.ResponseWriter, r *http.Request, next http.Handler) e
|
||||
next.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
remote, err := proxy.NewRemoteHelper(r.Context(), p.RegistryID)
|
||||
remote, err := proxy.NewRemoteHelper(r.Context(), p.RegistryID, proxy.WithSpeed(p.ProxyCacheSpeed()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ func TagsListMiddleware() func(http.Handler) http.Handler {
|
||||
return middleware.New(func(w http.ResponseWriter, r *http.Request, next http.Handler) {
|
||||
ctx := r.Context()
|
||||
|
||||
art, p, _, err := preCheck(ctx)
|
||||
art, p, _, err := preCheck(ctx, false)
|
||||
if err != nil {
|
||||
libhttp.SendError(w, err)
|
||||
return
|
||||
@ -69,7 +69,7 @@ func TagsListMiddleware() func(http.Handler) http.Handler {
|
||||
util.SendListTagsResponse(w, r, tags)
|
||||
}()
|
||||
|
||||
remote, err := proxy.NewRemoteHelper(ctx, p.RegistryID)
|
||||
remote, err := proxy.NewRemoteHelper(ctx, p.RegistryID, proxy.WithSpeed(p.ProxyCacheSpeed()))
|
||||
if err != nil {
|
||||
logger.Warningf("failed to get remote interface, error: %v, fallback to local tags", err)
|
||||
return
|
||||
|
@ -104,7 +104,7 @@ func (a *artifactAPI) ListArtifacts(ctx context.Context, params operation.ListAr
|
||||
|
||||
// set option
|
||||
option := option(params.WithTag, params.WithImmutableStatus,
|
||||
params.WithLabel, params.WithAccessory)
|
||||
params.WithLabel, params.WithAccessory, nil)
|
||||
|
||||
// get the total count of artifacts
|
||||
total, err := a.artCtl.Count(ctx, query)
|
||||
@ -138,7 +138,7 @@ func (a *artifactAPI) GetArtifact(ctx context.Context, params operation.GetArtif
|
||||
}
|
||||
// set option
|
||||
option := option(params.WithTag, params.WithImmutableStatus,
|
||||
params.WithLabel, params.WithAccessory)
|
||||
params.WithLabel, params.WithAccessory, nil)
|
||||
|
||||
// get the artifact
|
||||
artifact, err := a.artCtl.GetByReference(ctx, fmt.Sprintf("%s/%s", params.ProjectName, params.RepositoryName), params.Reference, option)
|
||||
@ -510,11 +510,12 @@ func (a *artifactAPI) RequireLabelInProject(ctx context.Context, projectID, labe
|
||||
return nil
|
||||
}
|
||||
|
||||
func option(withTag, withImmutableStatus, withLabel, withAccessory *bool) *artifact.Option {
|
||||
func option(withTag, withImmutableStatus, withLabel, withAccessory *bool, latestInRepository *bool) *artifact.Option {
|
||||
option := &artifact.Option{
|
||||
WithTag: true, // return the tag by default
|
||||
WithLabel: lib.BoolValue(withLabel),
|
||||
WithAccessory: true, // return the accessory by default
|
||||
WithTag: true, // return the tag by default
|
||||
WithLabel: lib.BoolValue(withLabel),
|
||||
WithAccessory: true, // return the accessory by default
|
||||
LatestInRepository: lib.BoolValue(latestInRepository),
|
||||
}
|
||||
|
||||
if withTag != nil {
|
||||
|
@ -49,6 +49,8 @@ func (a *Artifact) ToSwagger() *models.Artifact {
|
||||
PushTime: strfmt.DateTime(a.PushTime),
|
||||
ExtraAttrs: a.ExtraAttrs,
|
||||
Annotations: a.Annotations,
|
||||
ArtifactType: a.ArtifactType,
|
||||
RepositoryName: a.RepositoryName,
|
||||
}
|
||||
|
||||
for _, reference := range a.References {
|
||||
|
@ -48,6 +48,7 @@ func (r *Robot) ToSwagger() *models.Robot {
|
||||
Level: r.Level,
|
||||
Disable: r.Disabled,
|
||||
Editable: r.Editable,
|
||||
Creator: r.Creator,
|
||||
CreationTime: strfmt.DateTime(r.CreationTime),
|
||||
UpdateTime: strfmt.DateTime(r.UpdateTime),
|
||||
Permissions: perms,
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/common/security"
|
||||
"github.com/goharbor/harbor/src/common/security/local"
|
||||
robotSec "github.com/goharbor/harbor/src/common/security/robot"
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"github.com/goharbor/harbor/src/controller/p2p/preheat"
|
||||
"github.com/goharbor/harbor/src/controller/project"
|
||||
"github.com/goharbor/harbor/src/controller/quota"
|
||||
@ -52,6 +53,7 @@ import (
|
||||
"github.com/goharbor/harbor/src/pkg/retention/policy"
|
||||
"github.com/goharbor/harbor/src/pkg/robot"
|
||||
userModels "github.com/goharbor/harbor/src/pkg/user/models"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/handler/assembler"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/handler/model"
|
||||
"github.com/goharbor/harbor/src/server/v2.0/models"
|
||||
operation "github.com/goharbor/harbor/src/server/v2.0/restapi/operations/project"
|
||||
@ -63,6 +65,7 @@ const defaultDaysToRetentionForProxyCacheProject = 7
|
||||
func newProjectAPI() *projectAPI {
|
||||
return &projectAPI{
|
||||
auditMgr: audit.Mgr,
|
||||
artCtl: artifact.Ctl,
|
||||
metadataMgr: pkg.ProjectMetaMgr,
|
||||
userCtl: user.Ctl,
|
||||
repositoryCtl: repository.Ctl,
|
||||
@ -79,6 +82,7 @@ func newProjectAPI() *projectAPI {
|
||||
type projectAPI struct {
|
||||
BaseAPI
|
||||
auditMgr audit.Manager
|
||||
artCtl artifact.Controller
|
||||
metadataMgr metadata.Manager
|
||||
userCtl user.Controller
|
||||
repositoryCtl repository.Controller
|
||||
@ -155,6 +159,11 @@ func (a *projectAPI) CreateProject(ctx context.Context, params operation.CreateP
|
||||
}
|
||||
}
|
||||
|
||||
// ignore metadata.proxy_speed_kb for non-proxy-cache project
|
||||
if req.RegistryID == nil {
|
||||
req.Metadata.ProxySpeedKb = nil
|
||||
}
|
||||
|
||||
// ignore enable_content_trust metadata for proxy cache project
|
||||
// see https://github.com/goharbor/harbor/issues/12940 to get more info
|
||||
if req.RegistryID != nil {
|
||||
@ -547,6 +556,11 @@ func (a *projectAPI) UpdateProject(ctx context.Context, params operation.UpdateP
|
||||
}
|
||||
}
|
||||
|
||||
// ignore metadata.proxy_speed_kb for non-proxy-cache project
|
||||
if params.Project.Metadata != nil && !p.IsProxy() {
|
||||
params.Project.Metadata.ProxySpeedKb = nil
|
||||
}
|
||||
|
||||
// ignore enable_content_trust metadata for proxy cache project
|
||||
// see https://github.com/goharbor/harbor/issues/12940 to get more info
|
||||
if params.Project.Metadata != nil && p.IsProxy() {
|
||||
@ -660,6 +674,82 @@ func (a *projectAPI) SetScannerOfProject(ctx context.Context, params operation.S
|
||||
return operation.NewSetScannerOfProjectOK()
|
||||
}
|
||||
|
||||
func (a *projectAPI) ListArtifactsOfProject(ctx context.Context, params operation.ListArtifactsOfProjectParams) middleware.Responder {
|
||||
if err := a.RequireAuthenticated(ctx); err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
projectNameOrID := parseProjectNameOrID(params.ProjectNameOrID, params.XIsResourceName)
|
||||
if err := a.RequireProjectAccess(ctx, projectNameOrID, rbac.ActionList, rbac.ResourceArtifact); err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
// set query
|
||||
pro, err := a.projectCtl.Get(ctx, projectNameOrID)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
query, err := a.BuildQuery(ctx, params.Q, params.Sort, params.Page, params.PageSize)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
query.Keywords["ProjectID"] = pro.ProjectID
|
||||
|
||||
// set option
|
||||
option := option(params.WithTag, params.WithImmutableStatus,
|
||||
params.WithLabel, params.WithAccessory, params.LatestInRepository)
|
||||
|
||||
var total int64
|
||||
// list artifacts according to the query and option
|
||||
var arts []*artifact.Artifact
|
||||
if option.LatestInRepository {
|
||||
// ignore page & page_size
|
||||
_, hasMediaType := query.Keywords["media_type"]
|
||||
_, hasArtifactType := query.Keywords["artifact_type"]
|
||||
if hasMediaType == hasArtifactType {
|
||||
return a.SendError(ctx, errors.BadRequestError(fmt.Errorf("either 'media_type' or 'artifact_type' must be specified, but not both, when querying with latest_in_repository")))
|
||||
}
|
||||
|
||||
getCount := func() (int64, error) {
|
||||
var countQ *q.Query
|
||||
if query != nil {
|
||||
countQ = q.New(query.Keywords)
|
||||
}
|
||||
allArts, err := a.artCtl.ListWithLatest(ctx, countQ, nil)
|
||||
if err != nil {
|
||||
return int64(0), err
|
||||
}
|
||||
return int64(len(allArts)), nil
|
||||
}
|
||||
total, err = getCount()
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
arts, err = a.artCtl.ListWithLatest(ctx, query, option)
|
||||
} else {
|
||||
total, err = a.artCtl.Count(ctx, query)
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
arts, err = a.artCtl.List(ctx, query, option)
|
||||
}
|
||||
if err != nil {
|
||||
return a.SendError(ctx, err)
|
||||
}
|
||||
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{}
|
||||
artifact.Artifact = *art
|
||||
_ = assembler.WithArtifacts(artifact).Assemble(ctx)
|
||||
artifacts = append(artifacts, artifact.ToSwagger())
|
||||
}
|
||||
|
||||
return operation.NewListArtifactsOfProjectOK().
|
||||
WithXTotalCount(total).
|
||||
WithLink(a.Links(ctx, params.HTTPRequest.URL, total, query.PageNumber, query.PageSize).String()).
|
||||
WithPayload(artifacts)
|
||||
}
|
||||
|
||||
func (a *projectAPI) deletable(ctx context.Context, projectNameOrID interface{}) (*project.Project, *models.ProjectDeletable, error) {
|
||||
p, err := a.getProject(ctx, projectNameOrID)
|
||||
if err != nil {
|
||||
@ -712,6 +802,13 @@ func (a *projectAPI) validateProjectReq(ctx context.Context, req *models.Project
|
||||
if !permitted {
|
||||
return errors.BadRequestError(fmt.Errorf("unsupported registry type %s", string(registry.Type)))
|
||||
}
|
||||
|
||||
// validate metadata.proxy_speed_kb. It should be an int32
|
||||
if ps := req.Metadata.ProxySpeedKb; ps != nil {
|
||||
if _, err := strconv.ParseInt(*ps, 10, 32); err != nil {
|
||||
return errors.BadRequestError(nil).WithMessage(fmt.Sprintf("metadata.proxy_speed_kb should by an int32, but got: '%s', err: %s", *ps, err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.StorageLimit != nil {
|
||||
|
@ -155,6 +155,12 @@ func (p *projectMetadataAPI) validate(metas map[string]string) (map[string]strin
|
||||
return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid value: %s", value)
|
||||
}
|
||||
metas[proModels.ProMetaSeverity] = strings.ToLower(severity.String())
|
||||
case proModels.ProMetaProxySpeed:
|
||||
v, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid value: %s", value)
|
||||
}
|
||||
metas[proModels.ProMetaProxySpeed] = strconv.FormatInt(v, 10)
|
||||
default:
|
||||
return nil, errors.New(nil).WithCode(errors.BadRequestCode).WithMessage("invalid key: %s", key)
|
||||
}
|
||||
|
@ -62,12 +62,18 @@ func (rAPI *robotAPI) CreateRobot(ctx context.Context, params operation.CreateRo
|
||||
return rAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
sc, err := rAPI.GetSecurityContext(ctx)
|
||||
if err != nil {
|
||||
return rAPI.SendError(ctx, err)
|
||||
}
|
||||
|
||||
r := &robot.Robot{
|
||||
Robot: pkg.Robot{
|
||||
Name: params.Robot.Name,
|
||||
Description: params.Robot.Description,
|
||||
Duration: params.Robot.Duration,
|
||||
Visible: true,
|
||||
Creator: sc.GetUsername(),
|
||||
},
|
||||
Level: params.Robot.Level,
|
||||
}
|
||||
|
@ -296,6 +296,36 @@ func (_m *Controller) List(ctx context.Context, query *q.Query, option *artifact
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListWithLatest provides a mock function with given fields: ctx, query, option
|
||||
func (_m *Controller) ListWithLatest(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 ListWithLatest")
|
||||
}
|
||||
|
||||
var r0 []*artifact.Artifact
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query, *artifact.Option) ([]*artifact.Artifact, error)); ok {
|
||||
return rf(ctx, query, option)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query, *artifact.Option) []*artifact.Artifact); ok {
|
||||
r0 = rf(ctx, query, option)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*artifact.Artifact)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query, *artifact.Option) error); ok {
|
||||
r1 = rf(ctx, query, option)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RemoveLabel provides a mock function with given fields: ctx, artifactID, labelID
|
||||
func (_m *Controller) RemoveLabel(ctx context.Context, artifactID int64, labelID int64) error {
|
||||
ret := _m.Called(ctx, artifactID, labelID)
|
||||
|
@ -1,136 +0,0 @@
|
||||
// Code generated by mockery v2.22.1. DO NOT EDIT.
|
||||
|
||||
package libcache
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
cache "github.com/goharbor/harbor/src/lib/cache"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
time "time"
|
||||
)
|
||||
|
||||
// Cache is an autogenerated mock type for the Cache type
|
||||
type Cache struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Contains provides a mock function with given fields: ctx, key
|
||||
func (_m *Cache) Contains(ctx context.Context, key string) bool {
|
||||
ret := _m.Called(ctx, key)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok {
|
||||
r0 = rf(ctx, key)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, key
|
||||
func (_m *Cache) Delete(ctx context.Context, key string) error {
|
||||
ret := _m.Called(ctx, key)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(ctx, key)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Fetch provides a mock function with given fields: ctx, key, value
|
||||
func (_m *Cache) Fetch(ctx context.Context, key string, value interface{}) error {
|
||||
ret := _m.Called(ctx, key, value)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok {
|
||||
r0 = rf(ctx, key, value)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Ping provides a mock function with given fields: ctx
|
||||
func (_m *Cache) Ping(ctx context.Context) error {
|
||||
ret := _m.Called(ctx)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
|
||||
r0 = rf(ctx)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: ctx, key, value, expiration
|
||||
func (_m *Cache) Save(ctx context.Context, key string, value interface{}, expiration ...time.Duration) error {
|
||||
_va := make([]interface{}, len(expiration))
|
||||
for _i := range expiration {
|
||||
_va[_i] = expiration[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, key, value)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, interface{}, ...time.Duration) error); ok {
|
||||
r0 = rf(ctx, key, value, expiration...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Scan provides a mock function with given fields: ctx, match
|
||||
func (_m *Cache) Scan(ctx context.Context, match string) (cache.Iterator, error) {
|
||||
ret := _m.Called(ctx, match)
|
||||
|
||||
var r0 cache.Iterator
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (cache.Iterator, error)); ok {
|
||||
return rf(ctx, match)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) cache.Iterator); ok {
|
||||
r0 = rf(ctx, match)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(cache.Iterator)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, match)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewCache interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewCache creates a new instance of Cache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewCache(t mockConstructorTestingTNewCache) *Cache {
|
||||
mock := &Cache{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
@ -231,6 +231,36 @@ func (_m *Manager) ListReferences(ctx context.Context, query *q.Query) ([]*artif
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ListWithLatest provides a mock function with given fields: ctx, query
|
||||
func (_m *Manager) ListWithLatest(ctx context.Context, query *q.Query) ([]*artifact.Artifact, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListWithLatest")
|
||||
}
|
||||
|
||||
var r0 []*artifact.Artifact
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*artifact.Artifact, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*artifact.Artifact); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*artifact.Artifact)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, _a1, props
|
||||
func (_m *Manager) Update(ctx context.Context, _a1 *artifact.Artifact, props ...string) error {
|
||||
_va := make([]interface{}, len(props))
|
||||
|
@ -1,38 +1,123 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package artifactrash
|
||||
|
||||
import (
|
||||
"context"
|
||||
context "context"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/goharbor/harbor/src/pkg/artifactrash/model"
|
||||
model "github.com/goharbor/harbor/src/pkg/artifactrash/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// FakeManager is a fake tag manager that implement the src/pkg/tag.Manager interface
|
||||
type FakeManager struct {
|
||||
// Manager is an autogenerated mock type for the Manager type
|
||||
type Manager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func (f *FakeManager) Create(ctx context.Context, artifactrsh *model.ArtifactTrash) (id int64, err error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
// Create provides a mock function with given fields: ctx, artifactrsh
|
||||
func (_m *Manager) Create(ctx context.Context, artifactrsh *model.ArtifactTrash) (int64, error) {
|
||||
ret := _m.Called(ctx, artifactrsh)
|
||||
|
||||
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.ArtifactTrash) (int64, error)); ok {
|
||||
return rf(ctx, artifactrsh)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *model.ArtifactTrash) int64); ok {
|
||||
r0 = rf(ctx, artifactrsh)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *model.ArtifactTrash) error); ok {
|
||||
r1 = rf(ctx, artifactrsh)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (f *FakeManager) Delete(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
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)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Filter ...
|
||||
func (f *FakeManager) Filter(ctx context.Context, timeWindow int64) (arts []model.ArtifactTrash, err error) {
|
||||
args := f.Called()
|
||||
return args.Get(0).([]model.ArtifactTrash), args.Error(1)
|
||||
// Filter provides a mock function with given fields: ctx, timeWindow
|
||||
func (_m *Manager) Filter(ctx context.Context, timeWindow int64) ([]model.ArtifactTrash, error) {
|
||||
ret := _m.Called(ctx, timeWindow)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Filter")
|
||||
}
|
||||
|
||||
var r0 []model.ArtifactTrash
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) ([]model.ArtifactTrash, error)); ok {
|
||||
return rf(ctx, timeWindow)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) []model.ArtifactTrash); ok {
|
||||
r0 = rf(ctx, timeWindow)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]model.ArtifactTrash)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, timeWindow)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Flush ...
|
||||
func (f *FakeManager) Flush(ctx context.Context, timeWindow int64) (err error) {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Flush provides a mock function with given fields: ctx, timeWindow
|
||||
func (_m *Manager) Flush(ctx context.Context, timeWindow int64) error {
|
||||
ret := _m.Called(ctx, timeWindow)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Flush")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, timeWindow)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManager(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Manager {
|
||||
mock := &Manager{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,33 +1,89 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package chart
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
helm_chart "helm.sh/helm/v3/pkg/chart"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
chart "helm.sh/helm/v3/pkg/chart"
|
||||
|
||||
chartserver "github.com/goharbor/harbor/src/pkg/chart"
|
||||
pkgchart "github.com/goharbor/harbor/src/pkg/chart"
|
||||
)
|
||||
|
||||
// FakeOpertaor ...
|
||||
type FakeOpertaor struct {
|
||||
// Operator is an autogenerated mock type for the Operator type
|
||||
type Operator struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// GetDetails ...
|
||||
func (f *FakeOpertaor) GetDetails(content []byte) (*chartserver.VersionDetails, error) {
|
||||
args := f.Called()
|
||||
var chartDetails *chartserver.VersionDetails
|
||||
if args.Get(0) != nil {
|
||||
chartDetails = args.Get(0).(*chartserver.VersionDetails)
|
||||
// GetData provides a mock function with given fields: content
|
||||
func (_m *Operator) GetData(content []byte) (*chart.Chart, error) {
|
||||
ret := _m.Called(content)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetData")
|
||||
}
|
||||
return chartDetails, args.Error(1)
|
||||
|
||||
var r0 *chart.Chart
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func([]byte) (*chart.Chart, error)); ok {
|
||||
return rf(content)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func([]byte) *chart.Chart); ok {
|
||||
r0 = rf(content)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*chart.Chart)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func([]byte) error); ok {
|
||||
r1 = rf(content)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetData ...
|
||||
func (f *FakeOpertaor) GetData(content []byte) (*helm_chart.Chart, error) {
|
||||
args := f.Called()
|
||||
var chartData *helm_chart.Chart
|
||||
if args.Get(0) != nil {
|
||||
chartData = args.Get(0).(*helm_chart.Chart)
|
||||
// GetDetails provides a mock function with given fields: content
|
||||
func (_m *Operator) GetDetails(content []byte) (*pkgchart.VersionDetails, error) {
|
||||
ret := _m.Called(content)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetDetails")
|
||||
}
|
||||
return chartData, args.Error(1)
|
||||
|
||||
var r0 *pkgchart.VersionDetails
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func([]byte) (*pkgchart.VersionDetails, error)); ok {
|
||||
return rf(content)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func([]byte) *pkgchart.VersionDetails); ok {
|
||||
r0 = rf(content)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*pkgchart.VersionDetails)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func([]byte) error); ok {
|
||||
r1 = rf(content)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewOperator creates a new instance of Operator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewOperator(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Operator {
|
||||
mock := &Operator{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v2.22.1. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package distribution
|
||||
|
||||
@ -16,6 +16,10 @@ type Manifest struct {
|
||||
func (_m *Manifest) Payload() (string, []byte, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Payload")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 []byte
|
||||
var r2 error
|
||||
@ -49,6 +53,10 @@ func (_m *Manifest) Payload() (string, []byte, error) {
|
||||
func (_m *Manifest) References() []distribution.Descriptor {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for References")
|
||||
}
|
||||
|
||||
var r0 []distribution.Descriptor
|
||||
if rf, ok := ret.Get(0).(func() []distribution.Descriptor); ok {
|
||||
r0 = rf()
|
||||
@ -61,13 +69,12 @@ func (_m *Manifest) References() []distribution.Descriptor {
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewManifest interface {
|
||||
// NewManifest creates a new instance of Manifest. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManifest(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewManifest creates a new instance of Manifest. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewManifest(t mockConstructorTestingTNewManifest) *Manifest {
|
||||
}) *Manifest {
|
||||
mock := &Manifest{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
|
@ -1,20 +1,58 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package immutable
|
||||
|
||||
import (
|
||||
"context"
|
||||
context "context"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/selector"
|
||||
selector "github.com/goharbor/harbor/src/lib/selector"
|
||||
)
|
||||
|
||||
// FakeMatcher ...
|
||||
// FakeMatcher is an autogenerated mock type for the ImmutableTagMatcher type
|
||||
type FakeMatcher struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Match ...
|
||||
func (f *FakeMatcher) Match(ctx context.Context, pid int64, c selector.Candidate) (bool, error) {
|
||||
args := f.Called()
|
||||
return args.Bool(0), args.Error(1)
|
||||
// Match provides a mock function with given fields: ctx, pid, c
|
||||
func (_m *FakeMatcher) Match(ctx context.Context, pid int64, c selector.Candidate) (bool, error) {
|
||||
ret := _m.Called(ctx, pid, c)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Match")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, selector.Candidate) (bool, error)); ok {
|
||||
return rf(ctx, pid, c)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, selector.Candidate) bool); ok {
|
||||
r0 = rf(ctx, pid, c)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64, selector.Candidate) error); ok {
|
||||
r1 = rf(ctx, pid, c)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewFakeMatcher creates a new instance of FakeMatcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewFakeMatcher(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *FakeMatcher {
|
||||
mock := &FakeMatcher{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package instance
|
||||
|
||||
@ -7,27 +7,35 @@ import (
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
provider "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
)
|
||||
|
||||
// FakeManager is an autogenerated mock type for the Manager type
|
||||
type FakeManager struct {
|
||||
// Manager is an autogenerated mock type for the Manager type
|
||||
type Manager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeManager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
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 {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
@ -38,9 +46,13 @@ func (_m *FakeManager) Count(ctx context.Context, query *q.Query) (int64, error)
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
func (_m *FakeManager) Delete(ctx context.Context, id 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)
|
||||
@ -52,10 +64,18 @@ func (_m *FakeManager) Delete(ctx context.Context, id int64) error {
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *FakeManager) Get(ctx context.Context, id int64) (*provider.Instance, error) {
|
||||
func (_m *Manager) Get(ctx context.Context, id int64) (*provider.Instance, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Get")
|
||||
}
|
||||
|
||||
var r0 *provider.Instance
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*provider.Instance, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *provider.Instance); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
@ -64,7 +84,6 @@ func (_m *FakeManager) Get(ctx context.Context, id int64) (*provider.Instance, e
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
@ -75,10 +94,18 @@ func (_m *FakeManager) Get(ctx context.Context, id int64) (*provider.Instance, e
|
||||
}
|
||||
|
||||
// GetByName provides a mock function with given fields: ctx, name
|
||||
func (_m *FakeManager) GetByName(ctx context.Context, name string) (*provider.Instance, error) {
|
||||
func (_m *Manager) GetByName(ctx context.Context, name string) (*provider.Instance, error) {
|
||||
ret := _m.Called(ctx, name)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetByName")
|
||||
}
|
||||
|
||||
var r0 *provider.Instance
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) (*provider.Instance, error)); ok {
|
||||
return rf(ctx, name)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) *provider.Instance); ok {
|
||||
r0 = rf(ctx, name)
|
||||
} else {
|
||||
@ -87,7 +114,6 @@ func (_m *FakeManager) GetByName(ctx context.Context, name string) (*provider.In
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, name)
|
||||
} else {
|
||||
@ -98,10 +124,18 @@ func (_m *FakeManager) GetByName(ctx context.Context, name string) (*provider.In
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeManager) List(ctx context.Context, query *q.Query) ([]*provider.Instance, error) {
|
||||
func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*provider.Instance, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for List")
|
||||
}
|
||||
|
||||
var r0 []*provider.Instance
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*provider.Instance, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*provider.Instance); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
@ -110,7 +144,6 @@ func (_m *FakeManager) List(ctx context.Context, query *q.Query) ([]*provider.In
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
@ -121,17 +154,24 @@ func (_m *FakeManager) List(ctx context.Context, query *q.Query) ([]*provider.In
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: ctx, inst
|
||||
func (_m *FakeManager) Save(ctx context.Context, inst *provider.Instance) (int64, error) {
|
||||
func (_m *Manager) Save(ctx context.Context, inst *provider.Instance) (int64, error) {
|
||||
ret := _m.Called(ctx, inst)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Save")
|
||||
}
|
||||
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *provider.Instance) (int64, error)); ok {
|
||||
return rf(ctx, inst)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *provider.Instance) int64); ok {
|
||||
r0 = rf(ctx, inst)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *provider.Instance) error); ok {
|
||||
r1 = rf(ctx, inst)
|
||||
} else {
|
||||
@ -142,7 +182,7 @@ func (_m *FakeManager) Save(ctx context.Context, inst *provider.Instance) (int64
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, inst, props
|
||||
func (_m *FakeManager) Update(ctx context.Context, inst *provider.Instance, props ...string) error {
|
||||
func (_m *Manager) Update(ctx context.Context, inst *provider.Instance, props ...string) error {
|
||||
_va := make([]interface{}, len(props))
|
||||
for _i := range props {
|
||||
_va[_i] = props[_i]
|
||||
@ -152,6 +192,10 @@ func (_m *FakeManager) Update(ctx context.Context, inst *provider.Instance, prop
|
||||
_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, *provider.Instance, ...string) error); ok {
|
||||
r0 = rf(ctx, inst, props...)
|
||||
@ -161,3 +205,17 @@ func (_m *FakeManager) Update(ctx context.Context, inst *provider.Instance, prop
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManager(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Manager {
|
||||
mock := &Manager{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,33 +1,40 @@
|
||||
// Code generated by mockery v2.0.3. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
modelspolicy "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
modelspolicy "github.com/goharbor/harbor/src/pkg/p2p/preheat/models/policy"
|
||||
)
|
||||
|
||||
// FakeManager is an autogenerated mock type for the Manager type
|
||||
type FakeManager struct {
|
||||
// Manager is an autogenerated mock type for the Manager type
|
||||
type Manager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeManager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
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 {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
@ -38,17 +45,24 @@ func (_m *FakeManager) Count(ctx context.Context, query *q.Query) (int64, error)
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, schema
|
||||
func (_m *FakeManager) Create(ctx context.Context, schema *modelspolicy.Schema) (int64, error) {
|
||||
func (_m *Manager) Create(ctx context.Context, schema *modelspolicy.Schema) (int64, error) {
|
||||
ret := _m.Called(ctx, schema)
|
||||
|
||||
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, *modelspolicy.Schema) (int64, error)); ok {
|
||||
return rf(ctx, schema)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *modelspolicy.Schema) int64); ok {
|
||||
r0 = rf(ctx, schema)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *modelspolicy.Schema) error); ok {
|
||||
r1 = rf(ctx, schema)
|
||||
} else {
|
||||
@ -59,9 +73,13 @@ func (_m *FakeManager) Create(ctx context.Context, schema *modelspolicy.Schema)
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
func (_m *FakeManager) Delete(ctx context.Context, id 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)
|
||||
@ -73,10 +91,18 @@ func (_m *FakeManager) Delete(ctx context.Context, id int64) error {
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *FakeManager) Get(ctx context.Context, id int64) (*modelspolicy.Schema, error) {
|
||||
func (_m *Manager) Get(ctx context.Context, id int64) (*modelspolicy.Schema, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Get")
|
||||
}
|
||||
|
||||
var r0 *modelspolicy.Schema
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*modelspolicy.Schema, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *modelspolicy.Schema); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
@ -85,7 +111,6 @@ func (_m *FakeManager) Get(ctx context.Context, id int64) (*modelspolicy.Schema,
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
@ -95,22 +120,29 @@ func (_m *FakeManager) Get(ctx context.Context, id int64) (*modelspolicy.Schema,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetByName provides a mock function with given fields: ctx, projectId, name
|
||||
func (_m *FakeManager) GetByName(ctx context.Context, projectId int64, name string) (*modelspolicy.Schema, error) {
|
||||
ret := _m.Called(ctx, projectId, name)
|
||||
// GetByName provides a mock function with given fields: ctx, projectID, name
|
||||
func (_m *Manager) GetByName(ctx context.Context, projectID int64, name string) (*modelspolicy.Schema, error) {
|
||||
ret := _m.Called(ctx, projectID, name)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetByName")
|
||||
}
|
||||
|
||||
var r0 *modelspolicy.Schema
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, string) (*modelspolicy.Schema, error)); ok {
|
||||
return rf(ctx, projectID, name)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, string) *modelspolicy.Schema); ok {
|
||||
r0 = rf(ctx, projectId, name)
|
||||
r0 = rf(ctx, projectID, name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*modelspolicy.Schema)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64, string) error); ok {
|
||||
r1 = rf(ctx, projectId, name)
|
||||
r1 = rf(ctx, projectID, name)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
@ -119,10 +151,18 @@ func (_m *FakeManager) GetByName(ctx context.Context, projectId int64, name stri
|
||||
}
|
||||
|
||||
// ListPolicies provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeManager) ListPolicies(ctx context.Context, query *q.Query) ([]*modelspolicy.Schema, error) {
|
||||
func (_m *Manager) ListPolicies(ctx context.Context, query *q.Query) ([]*modelspolicy.Schema, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListPolicies")
|
||||
}
|
||||
|
||||
var r0 []*modelspolicy.Schema
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*modelspolicy.Schema, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*modelspolicy.Schema); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
@ -131,7 +171,6 @@ func (_m *FakeManager) ListPolicies(ctx context.Context, query *q.Query) ([]*mod
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
@ -142,10 +181,18 @@ func (_m *FakeManager) ListPolicies(ctx context.Context, query *q.Query) ([]*mod
|
||||
}
|
||||
|
||||
// ListPoliciesByProject provides a mock function with given fields: ctx, project, query
|
||||
func (_m *FakeManager) ListPoliciesByProject(ctx context.Context, project int64, query *q.Query) ([]*modelspolicy.Schema, error) {
|
||||
func (_m *Manager) ListPoliciesByProject(ctx context.Context, project int64, query *q.Query) ([]*modelspolicy.Schema, error) {
|
||||
ret := _m.Called(ctx, project, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListPoliciesByProject")
|
||||
}
|
||||
|
||||
var r0 []*modelspolicy.Schema
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) ([]*modelspolicy.Schema, error)); ok {
|
||||
return rf(ctx, project, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, *q.Query) []*modelspolicy.Schema); ok {
|
||||
r0 = rf(ctx, project, query)
|
||||
} else {
|
||||
@ -154,7 +201,6 @@ func (_m *FakeManager) ListPoliciesByProject(ctx context.Context, project int64,
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64, *q.Query) error); ok {
|
||||
r1 = rf(ctx, project, query)
|
||||
} else {
|
||||
@ -165,7 +211,7 @@ func (_m *FakeManager) ListPoliciesByProject(ctx context.Context, project int64,
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, schema, props
|
||||
func (_m *FakeManager) Update(ctx context.Context, schema *modelspolicy.Schema, props ...string) error {
|
||||
func (_m *Manager) Update(ctx context.Context, schema *modelspolicy.Schema, props ...string) error {
|
||||
_va := make([]interface{}, len(props))
|
||||
for _i := range props {
|
||||
_va[_i] = props[_i]
|
||||
@ -175,6 +221,10 @@ func (_m *FakeManager) Update(ctx context.Context, schema *modelspolicy.Schema,
|
||||
_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, *modelspolicy.Schema, ...string) error); ok {
|
||||
r0 = rf(ctx, schema, props...)
|
||||
@ -184,3 +234,17 @@ func (_m *FakeManager) Update(ctx context.Context, schema *modelspolicy.Schema,
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManager(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Manager {
|
||||
mock := &Manager{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
artifact "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Parser is an autogenerated mock type for the Parser type
|
||||
@ -19,6 +19,10 @@ type Parser struct {
|
||||
func (_m *Parser) Parse(ctx context.Context, _a1 *artifact.Artifact, manifest []byte) error {
|
||||
ret := _m.Called(ctx, _a1, manifest)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Parse")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []byte) error); ok {
|
||||
r0 = rf(ctx, _a1, manifest)
|
||||
@ -28,3 +32,17 @@ func (_m *Parser) Parse(ctx context.Context, _a1 *artifact.Artifact, manifest []
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewParser creates a new instance of Parser. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewParser(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Parser {
|
||||
mock := &Parser{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package processor
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
artifact "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
processor "github.com/goharbor/harbor/src/controller/artifact/processor"
|
||||
artifact "github.com/goharbor/harbor/src/pkg/artifact"
|
||||
)
|
||||
|
||||
// Processor is an autogenerated mock type for the Processor type
|
||||
@ -20,7 +21,15 @@ type Processor struct {
|
||||
func (_m *Processor) AbstractAddition(ctx context.Context, _a1 *artifact.Artifact, additionType string) (*processor.Addition, error) {
|
||||
ret := _m.Called(ctx, _a1, additionType)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AbstractAddition")
|
||||
}
|
||||
|
||||
var r0 *processor.Addition
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, string) (*processor.Addition, error)); ok {
|
||||
return rf(ctx, _a1, additionType)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, string) *processor.Addition); ok {
|
||||
r0 = rf(ctx, _a1, additionType)
|
||||
} else {
|
||||
@ -29,7 +38,6 @@ func (_m *Processor) AbstractAddition(ctx context.Context, _a1 *artifact.Artifac
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *artifact.Artifact, string) error); ok {
|
||||
r1 = rf(ctx, _a1, additionType)
|
||||
} else {
|
||||
@ -43,6 +51,10 @@ func (_m *Processor) AbstractAddition(ctx context.Context, _a1 *artifact.Artifac
|
||||
func (_m *Processor) AbstractMetadata(ctx context.Context, _a1 *artifact.Artifact, manifest []byte) error {
|
||||
ret := _m.Called(ctx, _a1, manifest)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for AbstractMetadata")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact, []byte) error); ok {
|
||||
r0 = rf(ctx, _a1, manifest)
|
||||
@ -57,6 +69,10 @@ func (_m *Processor) AbstractMetadata(ctx context.Context, _a1 *artifact.Artifac
|
||||
func (_m *Processor) GetArtifactType(ctx context.Context, _a1 *artifact.Artifact) string {
|
||||
ret := _m.Called(ctx, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetArtifactType")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact) string); ok {
|
||||
r0 = rf(ctx, _a1)
|
||||
@ -71,6 +87,10 @@ func (_m *Processor) GetArtifactType(ctx context.Context, _a1 *artifact.Artifact
|
||||
func (_m *Processor) ListAdditionTypes(ctx context.Context, _a1 *artifact.Artifact) []string {
|
||||
ret := _m.Called(ctx, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ListAdditionTypes")
|
||||
}
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *artifact.Artifact) []string); ok {
|
||||
r0 = rf(ctx, _a1)
|
||||
@ -82,3 +102,17 @@ func (_m *Processor) ListAdditionTypes(ctx context.Context, _a1 *artifact.Artifa
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewProcessor creates a new instance of Processor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewProcessor(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Processor {
|
||||
mock := &Processor{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,256 +0,0 @@
|
||||
// Code generated by mockery v2.1.0. DO NOT EDIT.
|
||||
|
||||
package scan
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
scan "github.com/goharbor/harbor/src/pkg/scan/dao/scan"
|
||||
)
|
||||
|
||||
// VulnerabilityRecordDao is an autogenerated mock type for the VulnerabilityRecordDao type
|
||||
type VulnerabilityRecordDao struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Create provides a mock function with given fields: ctx, vr
|
||||
func (_m *VulnerabilityRecordDao) Create(ctx context.Context, vr *scan.VulnerabilityRecord) (int64, error) {
|
||||
ret := _m.Called(ctx, vr)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *scan.VulnerabilityRecord) int64); ok {
|
||||
r0 = rf(ctx, vr)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *scan.VulnerabilityRecord) error); ok {
|
||||
r1 = rf(ctx, vr)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: ctx, vr
|
||||
func (_m *VulnerabilityRecordDao) Delete(ctx context.Context, vr *scan.VulnerabilityRecord) error {
|
||||
ret := _m.Called(ctx, vr)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *scan.VulnerabilityRecord) error); ok {
|
||||
r0 = rf(ctx, vr)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteForDigests provides a mock function with given fields: ctx, digests
|
||||
func (_m *VulnerabilityRecordDao) DeleteForDigests(ctx context.Context, digests ...string) (int64, error) {
|
||||
_va := make([]interface{}, len(digests))
|
||||
for _i := range digests {
|
||||
_va[_i] = digests[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, ...string) int64); ok {
|
||||
r0 = rf(ctx, digests...)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, ...string) error); ok {
|
||||
r1 = rf(ctx, digests...)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteForReport provides a mock function with given fields: ctx, reportUUID
|
||||
func (_m *VulnerabilityRecordDao) DeleteForReport(ctx context.Context, reportUUID string) (int64, error) {
|
||||
ret := _m.Called(ctx, reportUUID)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok {
|
||||
r0 = rf(ctx, reportUUID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, reportUUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteForScanner provides a mock function with given fields: ctx, registrationUUID
|
||||
func (_m *VulnerabilityRecordDao) DeleteForScanner(ctx context.Context, registrationUUID string) (int64, error) {
|
||||
ret := _m.Called(ctx, registrationUUID)
|
||||
|
||||
var r0 int64
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) int64); ok {
|
||||
r0 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetForReport provides a mock function with given fields: ctx, reportUUID
|
||||
func (_m *VulnerabilityRecordDao) GetForReport(ctx context.Context, reportUUID string) ([]*scan.VulnerabilityRecord, error) {
|
||||
ret := _m.Called(ctx, reportUUID)
|
||||
|
||||
var r0 []*scan.VulnerabilityRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*scan.VulnerabilityRecord); ok {
|
||||
r0 = rf(ctx, reportUUID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*scan.VulnerabilityRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, reportUUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetForScanner provides a mock function with given fields: ctx, registrationUUID
|
||||
func (_m *VulnerabilityRecordDao) GetForScanner(ctx context.Context, registrationUUID string) ([]*scan.VulnerabilityRecord, error) {
|
||||
ret := _m.Called(ctx, registrationUUID)
|
||||
|
||||
var r0 []*scan.VulnerabilityRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*scan.VulnerabilityRecord); ok {
|
||||
r0 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*scan.VulnerabilityRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetRecordIdsForScanner provides a mock function with given fields: ctx, registrationUUID
|
||||
func (_m *VulnerabilityRecordDao) GetRecordIdsForScanner(ctx context.Context, registrationUUID string) ([]int, error) {
|
||||
ret := _m.Called(ctx, registrationUUID)
|
||||
|
||||
var r0 []int
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []int); ok {
|
||||
r0 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]int)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, registrationUUID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// InsertForReport provides a mock function with given fields: ctx, reportUUID, vulnerabilityRecordIDs
|
||||
func (_m *VulnerabilityRecordDao) InsertForReport(ctx context.Context, reportUUID string, vulnerabilityRecordIDs ...int64) error {
|
||||
_va := make([]interface{}, len(vulnerabilityRecordIDs))
|
||||
for _i := range vulnerabilityRecordIDs {
|
||||
_va[_i] = vulnerabilityRecordIDs[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, reportUUID)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, ...int64) error); ok {
|
||||
r0 = rf(ctx, reportUUID, vulnerabilityRecordIDs...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *VulnerabilityRecordDao) List(ctx context.Context, query *q.Query) ([]*scan.VulnerabilityRecord, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*scan.VulnerabilityRecord
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*scan.VulnerabilityRecord); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*scan.VulnerabilityRecord)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, vr, cols
|
||||
func (_m *VulnerabilityRecordDao) Update(ctx context.Context, vr *scan.VulnerabilityRecord, cols ...string) error {
|
||||
_va := make([]interface{}, len(cols))
|
||||
for _i := range cols {
|
||||
_va[_i] = cols[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, vr)
|
||||
_ca = append(_ca, _va...)
|
||||
ret := _m.Called(_ca...)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *scan.VulnerabilityRecord, ...string) error); ok {
|
||||
r0 = rf(ctx, vr, cols...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package postprocessors
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// NativeScanReportConverter is an autogenerated mock type for the NativeScanReportConverter type
|
||||
type NativeScanReportConverter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// FromRelationalSchema provides a mock function with given fields: ctx, reportUUID, artifactDigest, reportSummary
|
||||
func (_m *NativeScanReportConverter) FromRelationalSchema(ctx context.Context, reportUUID string, artifactDigest string, reportSummary string) (string, error) {
|
||||
ret := _m.Called(ctx, reportUUID, artifactDigest, reportSummary)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for FromRelationalSchema")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (string, error)); ok {
|
||||
return rf(ctx, reportUUID, artifactDigest, reportSummary)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string) string); ok {
|
||||
r0 = rf(ctx, reportUUID, artifactDigest, reportSummary)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
|
||||
r1 = rf(ctx, reportUUID, artifactDigest, reportSummary)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// ToRelationalSchema provides a mock function with given fields: ctx, reportUUID, registrationUUID, digest, reportData
|
||||
func (_m *NativeScanReportConverter) ToRelationalSchema(ctx context.Context, reportUUID string, registrationUUID string, digest string, reportData string) (string, string, error) {
|
||||
ret := _m.Called(ctx, reportUUID, registrationUUID, digest, reportData)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for ToRelationalSchema")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 string
|
||||
var r2 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) (string, string, error)); ok {
|
||||
return rf(ctx, reportUUID, registrationUUID, digest, reportData)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string) string); ok {
|
||||
r0 = rf(ctx, reportUUID, registrationUUID, digest, reportData)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string) string); ok {
|
||||
r1 = rf(ctx, reportUUID, registrationUUID, digest, reportData)
|
||||
} else {
|
||||
r1 = ret.Get(1).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(2).(func(context.Context, string, string, string, string) error); ok {
|
||||
r2 = rf(ctx, reportUUID, registrationUUID, digest, reportData)
|
||||
} else {
|
||||
r2 = ret.Error(2)
|
||||
}
|
||||
|
||||
return r0, r1, r2
|
||||
}
|
||||
|
||||
// NewNativeScanReportConverter creates a new instance of NativeScanReportConverter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewNativeScanReportConverter(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *NativeScanReportConverter {
|
||||
mock := &NativeScanReportConverter{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package postprocessors
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// ScanReportV1ToV2Converter is an auto-generated mock type for converting native Harbor report in JSON
|
||||
// to relational schema
|
||||
type ScanReportV1ToV2Converter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// ToRelationalSchema is a mock implementation of the scan report conversion
|
||||
func (_c *ScanReportV1ToV2Converter) ToRelationalSchema(ctx context.Context, reportUUID string, registrationUUID string, digest string, reportData string) (string, string, error) {
|
||||
return "mockId", reportData, nil
|
||||
}
|
||||
|
||||
// ToRelationalSchema is a mock implementation of the scan report conversion
|
||||
func (_c *ScanReportV1ToV2Converter) FromRelationalSchema(ctx context.Context, reportUUID string, artifactDigest string, reportData string) (string, error) {
|
||||
return "mockId", nil
|
||||
}
|
@ -1,79 +1,208 @@
|
||||
// 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.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package tag
|
||||
|
||||
import (
|
||||
"context"
|
||||
context "context"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
modeltag "github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/q"
|
||||
"github.com/goharbor/harbor/src/pkg/tag/model/tag"
|
||||
q "github.com/goharbor/harbor/src/lib/q"
|
||||
)
|
||||
|
||||
// FakeManager is a fake tag manager that implement the src/pkg/tag.Manager interface
|
||||
type FakeManager struct {
|
||||
// Manager is an autogenerated mock type for the Manager type
|
||||
type Manager struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Count ...
|
||||
func (f *FakeManager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
}
|
||||
// Count provides a mock function with given fields: ctx, query
|
||||
func (_m *Manager) Count(ctx context.Context, query *q.Query) (int64, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
// List ...
|
||||
func (f *FakeManager) List(ctx context.Context, query *q.Query) ([]*tag.Tag, error) {
|
||||
args := f.Called()
|
||||
var tags []*tag.Tag
|
||||
if args.Get(0) != nil {
|
||||
tags = args.Get(0).([]*tag.Tag)
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Count")
|
||||
}
|
||||
return tags, args.Error(1)
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (f *FakeManager) Get(ctx context.Context, id int64) (*tag.Tag, error) {
|
||||
args := f.Called()
|
||||
var tg *tag.Tag
|
||||
if args.Get(0) != nil {
|
||||
tg = args.Get(0).(*tag.Tag)
|
||||
var r0 int64
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) (int64, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
return tg, args.Error(1)
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) int64); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Create ...
|
||||
func (f *FakeManager) Create(ctx context.Context, tag *tag.Tag) (int64, error) {
|
||||
args := f.Called()
|
||||
return int64(args.Int(0)), args.Error(1)
|
||||
// Create provides a mock function with given fields: ctx, _a1
|
||||
func (_m *Manager) Create(ctx context.Context, _a1 *modeltag.Tag) (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, *modeltag.Tag) (int64, error)); ok {
|
||||
return rf(ctx, _a1)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *modeltag.Tag) int64); ok {
|
||||
r0 = rf(ctx, _a1)
|
||||
} else {
|
||||
r0 = ret.Get(0).(int64)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *modeltag.Tag) error); ok {
|
||||
r1 = rf(ctx, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update ...
|
||||
func (f *FakeManager) Update(ctx context.Context, tag *tag.Tag, props ...string) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Delete provides a mock function with given fields: ctx, id
|
||||
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)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (f *FakeManager) Delete(ctx context.Context, id int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// DeleteOfArtifact provides a mock function with given fields: ctx, artifactID
|
||||
func (_m *Manager) DeleteOfArtifact(ctx context.Context, artifactID int64) error {
|
||||
ret := _m.Called(ctx, artifactID)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteOfArtifact")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, artifactID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteOfArtifact ...
|
||||
func (f *FakeManager) DeleteOfArtifact(ctx context.Context, artifactID int64) error {
|
||||
args := f.Called()
|
||||
return args.Error(0)
|
||||
// Get provides a mock function with given fields: ctx, id
|
||||
func (_m *Manager) Get(ctx context.Context, id int64) (*modeltag.Tag, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Get")
|
||||
}
|
||||
|
||||
var r0 *modeltag.Tag
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) (*modeltag.Tag, error)); ok {
|
||||
return rf(ctx, id)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) *modeltag.Tag); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*modeltag.Tag)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// List provides a mock function with given fields: ctx, query
|
||||
func (_m *Manager) List(ctx context.Context, query *q.Query) ([]*modeltag.Tag, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for List")
|
||||
}
|
||||
|
||||
var r0 []*modeltag.Tag
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) ([]*modeltag.Tag, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *q.Query) []*modeltag.Tag); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*modeltag.Tag)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *q.Query) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: ctx, _a1, props
|
||||
func (_m *Manager) Update(ctx context.Context, _a1 *modeltag.Tag, props ...string) error {
|
||||
_va := make([]interface{}, len(props))
|
||||
for _i := range props {
|
||||
_va[_i] = props[_i]
|
||||
}
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, ctx, _a1)
|
||||
_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, *modeltag.Tag, ...string) error); ok {
|
||||
r0 = rf(ctx, _a1, props...)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewManager(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Manager {
|
||||
mock := &Manager{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -1,24 +1,78 @@
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package registryctl
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
type Mockclient struct {
|
||||
// Client is an autogenerated mock type for the Client type
|
||||
type Client struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Health ...
|
||||
func (c *Mockclient) Health() error {
|
||||
return nil
|
||||
// DeleteBlob provides a mock function with given fields: reference
|
||||
func (_m *Client) DeleteBlob(reference string) error {
|
||||
ret := _m.Called(reference)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DeleteBlob")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(reference)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteBlob ...
|
||||
func (c *Mockclient) DeleteBlob(reference string) (err error) {
|
||||
return nil
|
||||
// DeleteManifest provides a mock function with given fields: repository, reference
|
||||
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)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteManifest ...
|
||||
func (c *Mockclient) DeleteManifest(repository, reference string) (err error) {
|
||||
return nil
|
||||
// Health provides a mock function with given fields:
|
||||
func (_m *Client) Health() error {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Health")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func() error); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewClient(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Client {
|
||||
mock := &Client{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ class Artifact(base.Base, object):
|
||||
if "with_scan_overview" in kwargs:
|
||||
params["with_scan_overview"] = kwargs["with_scan_overview"]
|
||||
params["x_accept_vulnerabilities"] = ",".join(report_mime_types)
|
||||
if "with_sbom_overview" in kwargs:
|
||||
params["with_sbom_overview"] = kwargs["with_sbom_overview"]
|
||||
if "with_immutable_status" in kwargs:
|
||||
params["with_immutable_status"] = kwargs["with_immutable_status"]
|
||||
if "with_accessory" in kwargs:
|
||||
@ -140,6 +142,29 @@ class Artifact(base.Base, object):
|
||||
return
|
||||
raise Exception("Scan image result is {}, not as expected {}.".format(scan_status, expected_scan_status))
|
||||
|
||||
def check_image_sbom_generation_result(self, project_name, repo_name, reference, expected_scan_status = "Success", **kwargs):
|
||||
timeout_count = 30
|
||||
scan_status=""
|
||||
while True:
|
||||
time.sleep(5)
|
||||
timeout_count = timeout_count - 1
|
||||
if (timeout_count == 0):
|
||||
break
|
||||
artifact = self.get_reference_info(project_name, repo_name, reference, **kwargs)
|
||||
if expected_scan_status in ["Not Scanned", "No SBOM Overview"]:
|
||||
if artifact.sbom_overview is None:
|
||||
if (timeout_count > 24):
|
||||
continue
|
||||
print("artifact SBOM is not generated.")
|
||||
return
|
||||
else:
|
||||
raise Exception("Artifact SBOM should not be generated {}.".format(artifact.sbom_overview))
|
||||
|
||||
scan_status = artifact.sbom_overview.scan_status
|
||||
if scan_status == expected_scan_status:
|
||||
return
|
||||
raise Exception("Generate image SBOM result is {}, not as expected {}.".format(scan_status, expected_scan_status))
|
||||
|
||||
def check_reference_exist(self, project_name, repo_name, reference, ignore_not_found = False, **kwargs):
|
||||
artifact = self.get_reference_info( project_name, repo_name, reference, ignore_not_found=ignore_not_found, **kwargs)
|
||||
return {
|
||||
|
@ -21,3 +21,18 @@ class Scan(base.Base, object):
|
||||
base._assert_status_code(expect_status_code, status_code)
|
||||
|
||||
return data
|
||||
|
||||
def sbom_generation_of_artifact(self, project_name, repo_name, reference, expect_status_code = 202, expect_response_body = None, **kwargs):
|
||||
try:
|
||||
req_param = dict(scan_type = {"scan_type":"sbom"})
|
||||
data, status_code, _ = self._get_client(**kwargs).scan_artifact_with_http_info(project_name, repo_name, reference, **req_param)
|
||||
except ApiException as e:
|
||||
base._assert_status_code(expect_status_code, e.status)
|
||||
if expect_response_body is not None:
|
||||
base._assert_status_body(expect_response_body, e.body)
|
||||
return
|
||||
|
||||
base._assert_status_code(expect_status_code, status_code)
|
||||
|
||||
return data
|
||||
|
||||
|
@ -22,4 +22,19 @@ class StopScan(base.Base, object):
|
||||
|
||||
base._assert_status_code(expect_status_code, status_code)
|
||||
|
||||
return data
|
||||
|
||||
def stop_sbom_generation_of_artifact(self, project_name, repo_name, reference, expect_status_code = 202, expect_response_body = None, **kwargs):
|
||||
try:
|
||||
scanType = v2_swagger_client.ScanType()
|
||||
scanType.scan_type = "sbom"
|
||||
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:
|
||||
base._assert_status_body(expect_response_body, e.body)
|
||||
return
|
||||
|
||||
base._assert_status_code(expect_status_code, status_code)
|
||||
|
||||
return data
|
@ -73,7 +73,7 @@ list_metadata = Permission("{}/projects/{}/metadatas".format(harbor_base_url, pr
|
||||
read_metadata = Permission("{}/projects/{}/metadatas/auto_scan".format(harbor_base_url, project_id), "GET", 200, metadata_payload)
|
||||
metadata_payload_for_update = { "auto_scan": "false" }
|
||||
update_metadata = Permission("{}/projects/{}/metadatas/auto_scan".format(harbor_base_url, project_id), "PUT", 200, metadata_payload_for_update)
|
||||
delete_metadata = Permission("{}/projects/{}/metadatas/auto_scan".format(harbor_base_url, project_id), "DELETE", 200, metadata_payload)
|
||||
delete_metadata = Permission("{}/projects/{}/metadatas/auto_scan".format(harbor_base_url, project_id), "DELETE", 200, metadata_payload_for_update)
|
||||
|
||||
# 4. Resource: repository actions: ['read', 'list', 'update', 'delete', 'pull', 'push']
|
||||
# note: pull and push are for docker cli, no API needs them
|
||||
@ -89,12 +89,17 @@ 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 = {
|
||||
vulnerability_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_payload)
|
||||
create_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, vulnerability_scan_payload)
|
||||
stop_scan = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, vulnerability_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)
|
||||
sbom_gen_payload = {
|
||||
"scan_type": "sbom"
|
||||
}
|
||||
create_sbom_generation = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, sbom_gen_payload)
|
||||
stop_sbom_generation = Permission("{}/projects/{}/repositories/{}/artifacts/{}/scan/stop".format(harbor_base_url, project_name, source_artifact_name, source_artifact_tag), "POST", 202, sbom_gen_payload)
|
||||
|
||||
# 7. Resource tag actions: ['list', 'create', 'delete']
|
||||
tag_payload = { "name": "test-{}".format(int(random.randint(1000, 9999))) }
|
||||
@ -240,7 +245,7 @@ resource_permissions = {
|
||||
"metadata": [create_metadata, list_metadata, read_metadata, update_metadata, delete_metadata],
|
||||
"repository": [list_repo, read_repo, update_repo, delete_repo],
|
||||
"artifact": [list_artifact, read_artifact, copy_artifact, delete_artifact],
|
||||
"scan": [create_scan, stop_scan, read_scan],
|
||||
"scan": [create_scan, stop_scan, read_scan, create_sbom_generation, stop_sbom_generation],
|
||||
"tag": [create_tag, list_tag, delete_tag],
|
||||
"accessory": [list_accessory],
|
||||
"artifact-addition": [read_artifact_addition_vul, read_artifact_addition_dependencies],
|
||||
|
@ -72,7 +72,7 @@ class TestProxyCache(unittest.TestCase):
|
||||
index_for_ctr = dict(image = "alpine", tag = "3.12.0")
|
||||
else:
|
||||
user_namespace = "nightly"
|
||||
registry = "https://cicd.harbor.vmwarecna.net"
|
||||
registry = "https://registry.goharbor.io"
|
||||
index_for_ctr = dict(image = "busybox", tag = "1.32.0")
|
||||
|
||||
registry_id, _ = self.registry.create_registry(registry, name=_random_name(registry_type), registry_type=registry_type, access_key = access_key, access_secret = access_secret, insecure=True, **ADMIN_CLIENT)
|
||||
|
@ -0,0 +1,87 @@
|
||||
from __future__ import absolute_import
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from testutils import harbor_server, suppress_urllib3_warning
|
||||
from testutils import TEARDOWN
|
||||
from testutils import ADMIN_CLIENT, BASE_IMAGE, BASE_IMAGE_ABS_PATH_NAME
|
||||
from library.project import Project
|
||||
from library.user import User
|
||||
from library.repository import Repository
|
||||
from library.repository import push_self_build_image_to_project
|
||||
from library.artifact import Artifact
|
||||
from library.scan import Scan
|
||||
|
||||
class TestSBOMGeneration(unittest.TestCase):
|
||||
@suppress_urllib3_warning
|
||||
def setUp(self):
|
||||
self.project= Project()
|
||||
self.user= User()
|
||||
self.artifact = Artifact()
|
||||
self.repo = Repository()
|
||||
self.scan = Scan()
|
||||
|
||||
self.url = ADMIN_CLIENT["endpoint"]
|
||||
self.user_password = "Aa123456"
|
||||
self.project_id, self.project_name, self.user_id, self.user_name, self.repo_name1 = [None] * 5
|
||||
self.user_id, self.user_name = self.user.create_user(user_password = self.user_password, **ADMIN_CLIENT)
|
||||
self.USER_CLIENT = dict(with_signature = True, with_immutable_status = True, endpoint = self.url, username = self.user_name, password = self.user_password, with_sbom_overview = True)
|
||||
|
||||
|
||||
#2. Create a new private project(PA) by user(UA);
|
||||
self.project_id, self.project_name = self.project.create_project(metadata = {"public": "false"}, **ADMIN_CLIENT)
|
||||
|
||||
#3. Add user(UA) as a member of project(PA) with project-admin role;
|
||||
self.project.add_project_members(self.project_id, user_id = self.user_id, **ADMIN_CLIENT)
|
||||
|
||||
@unittest.skipIf(TEARDOWN == False, "Test data won't be erased.")
|
||||
def do_tearDown(self):
|
||||
#1. Delete repository(RA) by user(UA);
|
||||
self.repo.delete_repository(self.project_name, self.repo_name1.split('/')[1], **self.USER_CLIENT)
|
||||
|
||||
#2. Delete project(PA);
|
||||
self.project.delete_project(self.project_id, **self.USER_CLIENT)
|
||||
|
||||
#3. Delete user(UA);
|
||||
self.user.delete_user(self.user_id, **ADMIN_CLIENT)
|
||||
|
||||
def testGenerateSBOMOfImageArtifact(self):
|
||||
"""
|
||||
Test case:
|
||||
Generate an SBOM of An Image Artifact
|
||||
Test step and expected result:
|
||||
1. Create a new user(UA);
|
||||
2. Create a new private project(PA) by user(UA);
|
||||
3. Add user(UA) as a member of project(PA) with project-admin role;
|
||||
4. Get private project of user(UA), user(UA) can see only one private project which is project(PA);
|
||||
5. Create a new repository(RA) and tag(TA) in project(PA) by user(UA);
|
||||
6. Send sbom generation of an image command and get tag(TA) information to check sbom generation result, it should be finished;
|
||||
Tear down:
|
||||
1. Delete repository(RA) by user(UA);
|
||||
2. Delete project(PA);
|
||||
3. Delete user(UA);
|
||||
"""
|
||||
|
||||
#4. Get private project of user(UA), user(UA) can see only one private project which is project(PA);
|
||||
self.project.projects_should_exist(dict(public=False), expected_count = 1,
|
||||
expected_project_id = self.project_id, **self.USER_CLIENT)
|
||||
|
||||
#Note: Please make sure that this Image has never been pulled before by any other cases,
|
||||
# so it is a not-scanned image right after repository creation.
|
||||
image = "docker"
|
||||
src_tag = "1.13"
|
||||
#5. Create a new repository(RA) and tag(TA) in project(PA) by user(UA);
|
||||
self.repo_name1, tag = push_self_build_image_to_project(self.project_name, harbor_server, self.user_name, self.user_password, image, src_tag)
|
||||
|
||||
#6. Send sbom generation of an image command and get tag(TA) information to check sbom generation result, it should be finished;
|
||||
self.scan.sbom_generation_of_artifact(self.project_name, self.repo_name1.split('/')[1], tag, **self.USER_CLIENT)
|
||||
self.artifact.check_image_sbom_generation_result(self.project_name, image, tag, **self.USER_CLIENT)
|
||||
|
||||
self.do_tearDown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestSuite(unittest.makeSuite(TestSBOMGeneration))
|
||||
result = unittest.TextTestRunner(sys.stdout, verbosity=2, failfast=True).run(suite)
|
||||
if not result.wasSuccessful():
|
||||
raise Exception(r"SBOM generation test failed: {}".format(result))
|
@ -0,0 +1,91 @@
|
||||
from __future__ import absolute_import
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from testutils import harbor_server, suppress_urllib3_warning
|
||||
from testutils import TEARDOWN
|
||||
from testutils import ADMIN_CLIENT, BASE_IMAGE, BASE_IMAGE_ABS_PATH_NAME
|
||||
from library.project import Project
|
||||
from library.user import User
|
||||
from library.repository import Repository
|
||||
from library.repository import push_self_build_image_to_project
|
||||
from library.artifact import Artifact
|
||||
from library.scan import Scan
|
||||
from library.scan_stop import StopScan
|
||||
|
||||
class TestStopSBOMGeneration(unittest.TestCase):
|
||||
@suppress_urllib3_warning
|
||||
def setUp(self):
|
||||
self.project= Project()
|
||||
self.user= User()
|
||||
self.artifact = Artifact()
|
||||
self.repo = Repository()
|
||||
self.scan = Scan()
|
||||
self.stop_scan = StopScan()
|
||||
|
||||
self.url = ADMIN_CLIENT["endpoint"]
|
||||
self.user_password = "Aa123456"
|
||||
self.project_id, self.project_name, self.user_id, self.user_name, self.repo_name1 = [None] * 5
|
||||
self.user_id, self.user_name = self.user.create_user(user_password = self.user_password, **ADMIN_CLIENT)
|
||||
self.USER_CLIENT = dict(with_signature = True, with_immutable_status = True, endpoint = self.url, username = self.user_name, password = self.user_password, with_sbom_overview = True)
|
||||
|
||||
|
||||
#2. Create a new private project(PA) by user(UA);
|
||||
self.project_id, self.project_name = self.project.create_project(metadata = {"public": "false"}, **ADMIN_CLIENT)
|
||||
|
||||
#3. Add user(UA) as a member of project(PA) with project-admin role;
|
||||
self.project.add_project_members(self.project_id, user_id = self.user_id, **ADMIN_CLIENT)
|
||||
|
||||
@unittest.skipIf(TEARDOWN == False, "Test data won't be erased.")
|
||||
def do_tearDown(self):
|
||||
#1. Delete repository(RA) by user(UA);
|
||||
self.repo.delete_repository(self.project_name, self.repo_name1.split('/')[1], **self.USER_CLIENT)
|
||||
|
||||
#2. Delete project(PA);
|
||||
self.project.delete_project(self.project_id, **self.USER_CLIENT)
|
||||
|
||||
#3. Delete user(UA);
|
||||
self.user.delete_user(self.user_id, **ADMIN_CLIENT)
|
||||
|
||||
def testStopSBOMGenerationOfImageArtifact(self):
|
||||
"""
|
||||
Test case:
|
||||
Stop SBOM Generation Of An Image Artifact
|
||||
Test step and expected result:
|
||||
1. Create a new user(UA);
|
||||
2. Create a new private project(PA) by user(UA);
|
||||
3. Add user(UA) as a member of project(PA) with project-admin role;
|
||||
4. Get private project of user(UA), user(UA) can see only one private project which is project(PA);
|
||||
5. Create a new repository(RA) and tag(TA) in project(PA) by user(UA);
|
||||
6. Send SBOM generation of an image command;
|
||||
7. Send stop SBOM generation of an image command.
|
||||
Tear down:
|
||||
1. Delete repository(RA) by user(UA);
|
||||
2. Delete project(PA);
|
||||
3. Delete user(UA);
|
||||
"""
|
||||
|
||||
#4. Get private project of user(UA), user(UA) can see only one private project which is project(PA);
|
||||
self.project.projects_should_exist(dict(public=False), expected_count = 1,
|
||||
expected_project_id = self.project_id, **self.USER_CLIENT)
|
||||
|
||||
#Note: Please make sure that this Image has never been pulled before by any other cases,
|
||||
# so it is a not-scanned image right after repository creation.
|
||||
image = "docker"
|
||||
src_tag = "1.13"
|
||||
#5. Create a new repository(RA) and tag(TA) in project(PA) by user(UA);
|
||||
self.repo_name1, tag = push_self_build_image_to_project(self.project_name, harbor_server, self.user_name, self.user_password, image, src_tag)
|
||||
|
||||
#6. Send SBOM generation of an image command;
|
||||
self.scan.sbom_generation_of_artifact(self.project_name, self.repo_name1.split('/')[1], tag, **self.USER_CLIENT)
|
||||
|
||||
#7. Send stop SBOM generation of an image command.
|
||||
self.stop_scan.stop_sbom_generation_of_artifact(self.project_name, self.repo_name1.split('/')[1], tag, **self.USER_CLIENT)
|
||||
|
||||
self.do_tearDown()
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = unittest.TestSuite(unittest.makeSuite(TestStopSBOMGeneration))
|
||||
result = unittest.TextTestRunner(sys.stdout, verbosity=2, failfast=True).run(suite)
|
||||
if not result.wasSuccessful():
|
||||
raise Exception(r"Stop SBOM generation test failed: {}".format(result))
|
@ -41,9 +41,20 @@ Stop Scan Artifact
|
||||
Retry Element Click ${stop_scan_artifact_btn}
|
||||
|
||||
Check Scan Artifact Job Status Is Stopped
|
||||
Wait Until Element Is Visible ${stopped_label}
|
||||
${job_status}= Get Text ${stopped_label}
|
||||
Should Be Equal As Strings '${job_status}' 'Scan stopped'
|
||||
Wait Until Element Is Visible ${scan_stopped_label}
|
||||
|
||||
Generate Artifact SBOM
|
||||
[Arguments] ${project} ${repo} ${label_xpath}=//clr-dg-row//label[contains(@class,'clr-control-label')][1]
|
||||
Go Into Repo ${project} ${repo}
|
||||
Retry Element Click ${label_xpath}
|
||||
Retry Element Click ${gen_artifact_sbom_btn}
|
||||
|
||||
Stop Gen Artifact SBOM
|
||||
Retry Element Click ${artifact_action_xpath}
|
||||
Retry Element Click ${stop_gen_artifact_sbom_btn}
|
||||
|
||||
Check Gen Artifact SBOM Job Status Is Stopped
|
||||
Wait Until Element Is Visible ${gen_sbom_stopped_label}
|
||||
|
||||
Refresh Repositories
|
||||
Retry Element Click ${refresh_repositories_xpath}
|
||||
|
@ -24,5 +24,8 @@ ${build_history_data} //clr-dg-row
|
||||
${push_image_command_btn} //hbr-push-image-button//button
|
||||
${scan_artifact_btn} //button[@id='scan-btn']
|
||||
${stop_scan_artifact_btn} //button[@id='stop-scan']
|
||||
${stopped_label} //span[@class='label stopped']
|
||||
${scan_stopped_label} //span[normalize-space()='Scan stopped']
|
||||
${gen_sbom_stopped_label} //span[normalize-space()='Generation stopped']
|
||||
${gen_artifact_sbom_btn} //button[@id='generate-sbom-btn']
|
||||
${stop_gen_artifact_sbom_btn} //button[@id='stop-sbom-btn']
|
||||
${refresh_repositories_xpath} //hbr-repository-gridview//span[contains(@class,'refresh-btn')]
|
@ -59,6 +59,41 @@ Enable Scan On Push
|
||||
Checkbox Should Be Selected //clr-checkbox-wrapper[@id='scan-image-on-push-wrapper']//input
|
||||
Retry Element Click ${project_config_save_btn}
|
||||
|
||||
Generate Repo SBOM
|
||||
[Arguments] ${tagname} ${status}
|
||||
Retry Element Click //clr-dg-row[contains(.,'${tagname}')]//label[contains(@class,'clr-control-label')]
|
||||
Retry Element Click //button[@id='generate-sbom-btn']
|
||||
Run Keyword If '${status}' == 'Succeed' Wait Until Element Is Visible //a[@title='SBOM details'] 300
|
||||
|
||||
Checkout And Review SBOM Details
|
||||
[Arguments] ${tagname}
|
||||
Retry Element Click //clr-dg-row[contains(.,'${tagname}')]//a[@title='SBOM details']
|
||||
# Download SBOM file
|
||||
Retry Element Click //button[@id='sbom-btn']
|
||||
${sbom_artifact_short_sha256}= Get Text //span[@class='margin-left-10px']
|
||||
${sbom_filename_raw}= Get Text //clr-dg-cell[contains(text(),'${sbom_artifact_short_sha256}')]
|
||||
${sbom_filename}= Replace String ${sbom_filename_raw} : _ count=-1
|
||||
${sbom_filename}= Replace String ${sbom_filename} / _ count=-1
|
||||
${sbom_json_path}= Set Variable ${download_directory}/${sbom_filename}.json
|
||||
Retry File Should Exist ${sbom_json_path}
|
||||
# Load the downloaded SBOM json file and verify the first N package records
|
||||
${sbom_json_content}= Load Json From File ${sbom_json_path}
|
||||
${items}= Get Value From JSON ${sbom_json_content} packages
|
||||
${items_length}= Get Length ${items}
|
||||
${first_n_records}= Evaluate min(5, ${items_length})
|
||||
FOR ${idx} IN RANGE 1 ${first_n_records}
|
||||
${item}= Get From List ${items} ${idx}
|
||||
Wait Until Element Is Visible //clr-dg-cell[normalize-space()='${item.name}']
|
||||
Wait Until Element Is Visible //clr-dg-cell[normalize-space()='${item.versionInfo}']
|
||||
Wait Until Element Is Visible //clr-dg-cell[normalize-space()='${item.licenseConcluded}']
|
||||
END
|
||||
|
||||
Enable Generating SBOM On Push
|
||||
Checkbox Should Not Be Selected //clr-checkbox-wrapper[@id='generate-sbom-on-push-wrapper']//input
|
||||
Retry Element Click //clr-checkbox-wrapper[@id='generate-sbom-on-push-wrapper']//label[contains(@class,'clr-control-label')]
|
||||
Checkbox Should Be Selected //clr-checkbox-wrapper[@id='generate-sbom-on-push-wrapper']//input
|
||||
Retry Element Click ${project_config_save_btn}
|
||||
|
||||
Vulnerability Not Ready Project Hint
|
||||
Sleep 2
|
||||
${element}= Set Variable xpath=//span[contains(@class, 'db-status-warning')]
|
||||
|
@ -417,6 +417,49 @@ Stop Scan All
|
||||
Stop Scan All Artifact
|
||||
Retry Action Keyword Check Scan All Artifact Job Status Is Stopped
|
||||
|
||||
Body Of Generate SBOM of An Image In The Repo
|
||||
[Arguments] ${image_argument} ${tag_argument}
|
||||
Init Chrome Driver
|
||||
|
||||
${d}= get current date result_format=%m%s
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Create An New Project And Go Into Project project${d}
|
||||
Push Image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${image_argument}:${tag_argument}
|
||||
Go Into Repo project${d} ${image_argument}
|
||||
Generate Repo SBOM ${tag_argument} Succeed
|
||||
Checkout And Review SBOM Details ${tag_argument}
|
||||
Close Browser
|
||||
|
||||
Body Of Generate Image SBOM On Push
|
||||
Init Chrome Driver
|
||||
${d}= get current date result_format=%m%s
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Create An New Project And Go Into Project project${d}
|
||||
Goto Project Config
|
||||
Enable Generating SBOM On Push
|
||||
Push Image ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} memcached
|
||||
Go Into Repo project${d} memcached
|
||||
Checkout And Review SBOM Details latest
|
||||
Close Browser
|
||||
|
||||
Body Of Stop SBOM Manual Generation
|
||||
Init Chrome Driver
|
||||
${d}= get current date result_format=%m%s
|
||||
${repo}= Set Variable goharbor/harbor-e2e-engine
|
||||
${tag}= Set Variable test-ui
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Create An New Project And Go Into Project project${d}
|
||||
Push Image With Tag ${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} project${d} ${repo} ${tag} ${tag}
|
||||
# stop generate sbom of an artifact
|
||||
Retry Action Keyword Stop SBOM Generation project${d} ${repo}
|
||||
Close Browser
|
||||
|
||||
Stop SBOM Generation
|
||||
[Arguments] ${project_name} ${repo}
|
||||
Generate Artifact SBOM ${project_name} ${repo}
|
||||
Stop Gen Artifact SBOM
|
||||
Retry Action Keyword Check Gen Artifact SBOM Job Status Is Stopped
|
||||
|
||||
Prepare Image Package Test Files
|
||||
[Arguments] ${files_path}
|
||||
${rc} ${output}= Run And Return Rc And Output bash tests/robot-cases/Group0-Util/prepare_imgpkg_test_files.sh ${files_path}
|
||||
|
@ -120,6 +120,14 @@ Test Case - Stop Scan All Images
|
||||
[Tags] stop_scan_all
|
||||
Harbor API Test ./tests/apitests/python/test_system_level_stop_scan_all.py
|
||||
|
||||
Test Case - Generate SBOM Of An Image
|
||||
[Tags] generate_sbom
|
||||
Harbor API Test ./tests/apitests/python/test_sbom_generation_of_image_artifact.py
|
||||
|
||||
Test Case - Stop Generating SBOM Of An Image
|
||||
[Tags] stop_generating_sbom
|
||||
Harbor API Test ./tests/apitests/python/test_stop_sbom_generation_of_image_artifact.py
|
||||
|
||||
Test Case - Registry API
|
||||
[Tags] reg_api
|
||||
Harbor API Test ./tests/apitests/python/test_registry_api.py
|
||||
|
@ -244,7 +244,7 @@ Test Case - User View Logs
|
||||
Create An New Project And Go Into Project project${d}
|
||||
Logout Harbor
|
||||
|
||||
Body Of Replication Of Pull Images from Registry To Self harbor https://cicd.harbor.vmwarecna.net ${null} ${null} nightly/${replication_image} project${d} N Flatten 1 Level @{target_images}
|
||||
Body Of Replication Of Pull Images from Registry To Self harbor https://${LOCAL_REGISTRY} ${null} ${null} nightly/${replication_image} project${d} N Flatten 1 Level @{target_images}
|
||||
|
||||
Push image ${ip} ${user} ${pwd} project${d} ${img}:${tag}
|
||||
Pull image ${ip} ${user} ${pwd} project${d} ${replication_image}:${replication_tag}
|
||||
@ -1118,7 +1118,7 @@ Test Case - Job Service Dashboard Workers
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Create An New Project And Go Into Project ${project_name}
|
||||
Switch to Registries
|
||||
Create A New Endpoint harbor ${endpoint_name} https://cicd.harbor.vmwarecna.net ${null} ${null}
|
||||
Create A New Endpoint harbor ${endpoint_name} https://${LOCAL_REGISTRY} ${null} ${null}
|
||||
Switch To Replication Manage
|
||||
Create A Rule With Existing Endpoint ${rule_name} pull nightly/test_replication image ${endpoint_name} ${project_name} bandwidth=50 bandwidth_unit=Mbps
|
||||
Select Rule And Replicate ${rule_name}
|
||||
|
@ -119,7 +119,7 @@ Test Case - Ldap Group Admin DN Setting
|
||||
Logout Harbor
|
||||
Sign In Harbor ${HARBOR_URL} mike zhu88jie
|
||||
Switch To Registries
|
||||
Create A New Endpoint harbor edp1${d} https://cicd.harbor.vmwarecna.net ${null} ${null} Y
|
||||
Create A New Endpoint harbor edp1${d} https://${LOCAL_REGISTRY} ${null} ${null} Y
|
||||
|
||||
|
||||
Test Case - Run LDAP Group Related API Test
|
||||
|
@ -120,7 +120,7 @@ Test Case - OIDC Group User
|
||||
${pwd}= Set Variable ${admin_pwd}
|
||||
Sign In Harbor With OIDC User ${HARBOR_URL} username=${admin_user} password=${admin_pwd} login_with_provider=ldap
|
||||
Switch To Registries
|
||||
Create A New Endpoint harbor test_oidc_admin https://cicd.harbor.vmwarecna.net ${null} ${null} Y
|
||||
Create A New Endpoint harbor test_oidc_admin https://${LOCAL_REGISTRY} ${null} ${null} Y
|
||||
${secret}= Get Secrete By API ${HARBOR_URL} username=${admin_user}
|
||||
Push image ${ip} ${admin_user} ${secret} library ${image}
|
||||
Logout Harbor
|
||||
|
@ -93,7 +93,7 @@ Test Case - Replication Rule Edit
|
||||
${cron_str}= Set Variable 0 0 0 * * 0
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Switch To Registries
|
||||
Create A New Endpoint harbor ${endpoint1} https://cicd.harbor.vmwarecna.net ${null} ${null} Y
|
||||
Create A New Endpoint harbor ${endpoint1} https://${LOCAL_REGISTRY} ${null} ${null} Y
|
||||
Create A New Endpoint harbor ${endpoint2} https://${ip} ${HARBOR_ADMIN} ${HARBOR_PASSWORD} Y
|
||||
Switch To Replication Manage
|
||||
Create A Rule With Existing Endpoint ${rule_name_old} pull nightly/a* image ${endpoint1} project${d}
|
||||
@ -312,7 +312,7 @@ Test Case - Replication Of Pull Manifest List and CNAB from Harbor To Self
|
||||
${image2}= Get From Dictionary ${image2_with_tag} image
|
||||
${image3}= Get From Dictionary ${image3_with_tag} image
|
||||
@{target_images}= Create List '&{image1_with_tag}' '&{image2_with_tag}' '&{image3_with_tag}'
|
||||
Body Of Replication Of Pull Images from Registry To Self harbor https://cicd.harbor.vmwarecna.net ${null} ${null} nightly/{${image1},${image2},${image3}} ${null} Y Flatten 1 Level @{target_images}
|
||||
Body Of Replication Of Pull Images from Registry To Self harbor https://${LOCAL_REGISTRY} ${null} ${null} nightly/{${image1},${image2},${image3}} ${null} Y Flatten 1 Level @{target_images}
|
||||
|
||||
Test Case - Image Namespace Level Flattening
|
||||
[tags] flattening
|
||||
|
@ -28,7 +28,7 @@ ${HARBOR_ADMIN} admin
|
||||
Test Case - Proxy Cache
|
||||
[Tags] proxy_cache
|
||||
${d}= Get Current Date result_format=%m%s
|
||||
${registry}= Set Variable https://cicd.harbor.vmwarecna.net
|
||||
${registry}= Set Variable https://${LOCAL_REGISTRY}
|
||||
${user_namespace}= Set Variable nightly
|
||||
${image}= Set Variable for_proxy
|
||||
${tag}= Set Variable 1.0
|
||||
@ -156,7 +156,7 @@ Test Case - Replication Schedule Job
|
||||
Sign In Harbor ${HARBOR_URL} ${HARBOR_ADMIN} ${HARBOR_PASSWORD}
|
||||
Create An New Project And Go Into Project ${project_name}
|
||||
Switch To Registries
|
||||
Create A New Endpoint harbor e${d} https://cicd.harbor.vmwarecna.net ${null} ${null} Y
|
||||
Create A New Endpoint harbor e${d} https://${LOCAL_REGISTRY} ${null} ${null} Y
|
||||
Switch To Replication Manage
|
||||
${flag}= Set Variable ${false}
|
||||
FOR ${i} IN RANGE 999999
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user