Merge branch 'main' into dependabot/go_modules/src/github.com/volcengine/volcengine-go-sdk-1.1.17
Code scanning - action / CodeQL-Build (push) Has been cancelled Details
CI / UTTEST (push) Has been cancelled Details
CI / APITEST_DB (push) Has been cancelled Details
CI / APITEST_DB_PROXY_CACHE (push) Has been cancelled Details
CI / APITEST_LDAP (push) Has been cancelled Details
CI / OFFLINE (push) Has been cancelled Details
CI / UI_UT (push) Has been cancelled Details

This commit is contained in:
miner 2025-06-20 17:20:53 +08:00 committed by GitHub
commit 579f14bb29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
85 changed files with 314 additions and 309 deletions

View File

@ -27,7 +27,7 @@ func TestMaxOpenConns(t *testing.T) {
queryNum := 200 queryNum := 200
results := make([]bool, queryNum) results := make([]bool, queryNum)
for i := 0; i < queryNum; i++ { for i := range queryNum {
wg.Add(1) wg.Add(1)
go func(i int) { go func(i int) {
defer wg.Done() defer wg.Done()

View File

@ -142,7 +142,7 @@ func ArrayEqual(arrayA, arrayB []int) bool {
return false return false
} }
size := len(arrayA) size := len(arrayA)
for i := 0; i < size; i++ { for i := range size {
if arrayA[i] != arrayB[i] { if arrayA[i] != arrayB[i] {
return false return false
} }

View File

@ -119,7 +119,7 @@ func BenchmarkProjectEvaluator(b *testing.B) {
resource := NewNamespace(public.ProjectID).Resource(rbac.ResourceRepository) resource := NewNamespace(public.ProjectID).Resource(rbac.ResourceRepository)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
evaluator.HasPermission(context.TODO(), resource, rbac.ActionPull) evaluator.HasPermission(context.TODO(), resource, rbac.ActionPull)
} }
} }

View File

@ -75,7 +75,7 @@ func GenerateRandomStringWithLen(length int) string {
if err != nil { if err != nil {
log.Warningf("Error reading random bytes: %v", err) log.Warningf("Error reading random bytes: %v", err)
} }
for i := 0; i < length; i++ { for i := range length {
result[i] = chars[int(result[i])%l] result[i] = chars[int(result[i])%l]
} }
return string(result) return string(result)
@ -337,13 +337,3 @@ func MostMatchSorter(a, b string, matchWord string) bool {
func IsLocalPath(path string) bool { func IsLocalPath(path string) bool {
return len(path) == 0 || (strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "//")) return len(path) == 0 || (strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "//"))
} }
// StringInSlice check if the string is in the slice
func StringInSlice(str string, slice []string) bool {
for _, s := range slice {
if s == str {
return true
}
}
return false
}

View File

@ -66,8 +66,7 @@ func parseV1alpha1SkipList(artifact *artifact.Artifact, manifest *v1.Manifest) {
skipListAnnotationKey := fmt.Sprintf("%s.%s.%s", AnnotationPrefix, V1alpha1, SkipList) skipListAnnotationKey := fmt.Sprintf("%s.%s.%s", AnnotationPrefix, V1alpha1, SkipList)
skipList, ok := manifest.Config.Annotations[skipListAnnotationKey] skipList, ok := manifest.Config.Annotations[skipListAnnotationKey]
if ok { if ok {
skipKeyList := strings.Split(skipList, ",") for skipKey := range strings.SplitSeq(skipList, ",") {
for _, skipKey := range skipKeyList {
delete(metadata, skipKey) delete(metadata, skipKey)
} }
artifact.ExtraAttrs = metadata artifact.ExtraAttrs = metadata

View File

@ -156,7 +156,7 @@ func TestAddNode(t *testing.T) {
// Verify the path exists. // Verify the path exists.
current := root current := root
parts := filepath.Clean(tt.path) parts := filepath.Clean(tt.path)
for _, part := range strings.Split(parts, string(filepath.Separator)) { for part := range strings.SplitSeq(parts, string(filepath.Separator)) {
if part == "" { if part == "" {
continue continue
} }

View File

@ -232,7 +232,7 @@ func (suite *ControllerTestSuite) TestGet() {
func (suite *ControllerTestSuite) TestSync() { func (suite *ControllerTestSuite) TestSync() {
var references []distribution.Descriptor var references []distribution.Descriptor
for i := 0; i < 5; i++ { for i := range 5 {
references = append(references, distribution.Descriptor{ references = append(references, distribution.Descriptor{
MediaType: fmt.Sprintf("media type %d", i), MediaType: fmt.Sprintf("media type %d", i),
Digest: suite.Digest(), Digest: suite.Digest(),

View File

@ -206,7 +206,7 @@ func maxValueLimitedByLength(length int) int64 {
var value int64 var value int64
// the times for multiple, should *10 for every time // the times for multiple, should *10 for every time
times := 1 times := 1
for i := 0; i < length; i++ { for range length {
value = value + int64(9*times) value = value + int64(9*times)
times = times * 10 times = times * 10
} }

View File

@ -129,7 +129,7 @@ func constructScanImagePayload(ctx context.Context, event *event.ScanImageEvent,
// Wait for reasonable time to make sure the report is ready // Wait for reasonable time to make sure the report is ready
// Interval=500ms and total time = 5s // Interval=500ms and total time = 5s
// If the report is still not ready in the total time, then failed at then // If the report is still not ready in the total time, then failed at then
for i := 0; i < 10; i++ { for range 10 {
// First check in case it is ready // First check in case it is ready
if re, err := scan.DefaultController.GetReport(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}); err == nil { if re, err := scan.DefaultController.GetReport(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}); err == nil {
if len(re) > 0 && len(re[0].Report) > 0 { if len(re) > 0 && len(re[0].Report) > 0 {

View File

@ -48,7 +48,7 @@ func (c *controller) GetHealth(_ context.Context) *OverallHealthStatus {
for name, checker := range registry { for name, checker := range registry {
go check(name, checker, timeout, ch) go check(name, checker, timeout, ch)
} }
for i := 0; i < len(registry); i++ { for range len(registry) {
componentStatus := <-ch componentStatus := <-ch
if len(componentStatus.Error) != 0 { if len(componentStatus.Error) != 0 {
isHealthy = false isHealthy = false

View File

@ -17,6 +17,7 @@ package jobmonitor
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"strings" "strings"
"time" "time"
@ -278,12 +279,7 @@ func (w *monitorController) ListQueues(ctx context.Context) ([]*jm.Queue, error)
} }
func skippedUnusedJobType(jobType string) bool { func skippedUnusedJobType(jobType string) bool {
for _, t := range skippedJobTypes { return slices.Contains(skippedJobTypes, jobType)
if jobType == t {
return true
}
}
return false
} }
func (w *monitorController) PauseJobQueues(ctx context.Context, jobType string) error { func (w *monitorController) PauseJobQueues(ctx context.Context, jobType string) error {

View File

@ -131,7 +131,7 @@ func (m *ManifestListCache) push(ctx context.Context, repo, reference string, ma
// if time exceed, then push a updated manifest list which contains existing manifest // if time exceed, then push a updated manifest list which contains existing manifest
var newMan distribution.Manifest var newMan distribution.Manifest
var err error var err error
for n := 0; n < maxManifestListWait; n++ { for range maxManifestListWait {
log.Debugf("waiting for the manifest ready, repo %v, tag:%v", repo, reference) log.Debugf("waiting for the manifest ready, repo %v, tag:%v", repo, reference)
time.Sleep(sleepIntervalSec * time.Second) time.Sleep(sleepIntervalSec * time.Second)
newMan, err = m.updateManifestList(ctx, repo, man) newMan, err = m.updateManifestList(ctx, repo, man)
@ -177,7 +177,7 @@ type ManifestCache struct {
// CacheContent ... // CacheContent ...
func (m *ManifestCache) CacheContent(ctx context.Context, remoteRepo string, man distribution.Manifest, art lib.ArtifactInfo, r RemoteInterface, _ string) { func (m *ManifestCache) CacheContent(ctx context.Context, remoteRepo string, man distribution.Manifest, art lib.ArtifactInfo, r RemoteInterface, _ string) {
var waitBlobs []distribution.Descriptor var waitBlobs []distribution.Descriptor
for n := 0; n < maxManifestWait; n++ { for n := range maxManifestWait {
time.Sleep(sleepIntervalSec * time.Second) time.Sleep(sleepIntervalSec * time.Second)
waitBlobs = m.local.CheckDependencies(ctx, art.Repository, man) waitBlobs = m.local.CheckDependencies(ctx, art.Repository, man)
if len(waitBlobs) == 0 { if len(waitBlobs) == 0 {

View File

@ -78,13 +78,13 @@ func (suite *RefreshForProjectsTestSuite) TestRefreshForProjects() {
startProjectID := rand.Int63() startProjectID := rand.Int63()
var firstPageProjects, secondPageProjects []*models.Project var firstPageProjects, secondPageProjects []*models.Project
for i := 0; i < 50; i++ { for i := range 50 {
firstPageProjects = append(firstPageProjects, &models.Project{ firstPageProjects = append(firstPageProjects, &models.Project{
ProjectID: startProjectID + int64(i), ProjectID: startProjectID + int64(i),
}) })
} }
for i := 0; i < 10; i++ { for i := range 10 {
secondPageProjects = append(secondPageProjects, &models.Project{ secondPageProjects = append(secondPageProjects, &models.Project{
ProjectID: startProjectID + 50 + int64(i), ProjectID: startProjectID + 50 + int64(i),
}) })

View File

@ -423,10 +423,7 @@ func (t *transfer) copyBlobByChunk(srcRepo, dstRepo, digest string, sizeFromDesc
// update the start and end for upload // update the start and end for upload
*start = *end + 1 *start = *end + 1
// since both ends are closed intervals, it is necessary to subtract one byte // since both ends are closed intervals, it is necessary to subtract one byte
*end = *start + replicationChunkSize - 1 *end = min(*start+replicationChunkSize-1, endRange)
if *end >= endRange {
*end = endRange
}
t.logger.Infof("copying the blob chunk: %d-%d/%d", *start, *end, sizeFromDescriptor) t.logger.Infof("copying the blob chunk: %d-%d/%d", *start, *end, sizeFromDescriptor)
_, data, err := t.src.PullBlobChunk(srcRepo, digest, sizeFromDescriptor, *start, *end) _, data, err := t.src.PullBlobChunk(srcRepo, digest, sizeFromDescriptor, *start, *end)

View File

@ -16,6 +16,7 @@ package scan
import ( import (
"context" "context"
"slices"
"github.com/goharbor/harbor/src/controller/artifact" "github.com/goharbor/harbor/src/controller/artifact"
"github.com/goharbor/harbor/src/controller/artifact/processor/image" "github.com/goharbor/harbor/src/controller/artifact/processor/image"
@ -125,10 +126,8 @@ func hasCapability(r *models.Registration, a *artifact.Artifact) bool {
// use allowlist here because currently only docker image is supported by the scanner // use allowlist here because currently only docker image is supported by the scanner
// https://github.com/goharbor/pluggable-scanner-spec/issues/2 // https://github.com/goharbor/pluggable-scanner-spec/issues/2
allowlist := []string{image.ArtifactTypeImage} allowlist := []string{image.ArtifactTypeImage}
for _, t := range allowlist { if slices.Contains(allowlist, a.Type) {
if a.Type == t { return r.HasCapability(a.ManifestMediaType)
return r.HasCapability(a.ManifestMediaType)
}
} }
return false return false

View File

@ -17,6 +17,7 @@ package scanner
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"sync" "sync"
"time" "time"
@ -383,13 +384,7 @@ var (
) )
func isReservedName(name string) bool { func isReservedName(name string) bool {
for _, reservedName := range reservedNames { return slices.Contains(reservedNames, name)
if name == reservedName {
return true
}
}
return false
} }
// MetadataResult metadata or error saved in cache // MetadataResult metadata or error saved in cache

View File

@ -150,7 +150,7 @@ func (l *Auth) attachGroupParallel(ctx context.Context, ldapUsers []model.User,
g := new(errgroup.Group) g := new(errgroup.Group)
g.SetLimit(workerCount) g.SetLimit(workerCount)
for i := 0; i < workerCount; i++ { for i := range workerCount {
curIndex := i curIndex := i
g.Go(func() error { g.Go(func() error {
userGroups := make([]ugModel.UserGroup, 0) userGroups := make([]ugModel.UserGroup, 0)

View File

@ -24,6 +24,7 @@ import (
"os" "os"
"path" "path"
"runtime" "runtime"
"slices"
"testing" "testing"
"github.com/docker/distribution/registry/auth/token" "github.com/docker/distribution/registry/auth/token"
@ -239,10 +240,8 @@ func (f *fakeSecurityContext) IsSolutionUser() bool {
} }
func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resource rbac.Resource) bool { func (f *fakeSecurityContext) Can(ctx context.Context, action rbac.Action, resource rbac.Resource) bool {
if actions, ok := f.rcActions[resource]; ok { if actions, ok := f.rcActions[resource]; ok {
for _, a := range actions { if slices.Contains(actions, action) {
if a == action { return true
return true
}
} }
} }
return false return false

View File

@ -16,6 +16,7 @@ package session
import ( import (
"encoding/gob" "encoding/gob"
"maps"
"github.com/beego/beego/v2/server/web/session" "github.com/beego/beego/v2/server/web/session"
@ -51,14 +52,10 @@ func (*gobCodec) Decode(data []byte, v any) error {
switch in := v.(type) { switch in := v.(type) {
case map[any]any: case map[any]any:
for k, v := range vm { maps.Copy(in, vm)
in[k] = v
}
case *map[any]any: case *map[any]any:
m := *in m := *in
for k, v := range vm { maps.Copy(m, vm)
m[k] = v
}
default: default:
return errors.Errorf("object type invalid, %#v", v) return errors.Errorf("object type invalid, %#v", v)
} }

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"maps"
"math" "math"
"sync" "sync"
"time" "time"
@ -116,9 +117,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
// Copy properties // Copy properties
if len(c.properties) > 0 { if len(c.properties) > 0 {
for k, v := range c.properties { maps.Copy(jContext.properties, c.properties)
jContext.properties[k] = v
}
} }
// Refresh config properties // Refresh config properties
@ -128,9 +127,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
} }
props := c.cfgMgr.GetAll(c.sysContext) props := c.cfgMgr.GetAll(c.sysContext)
for k, v := range props { maps.Copy(jContext.properties, props)
jContext.properties[k] = v
}
// Set loggers for job // Set loggers for job
c.lock.Lock() c.lock.Lock()
@ -199,10 +196,7 @@ func createLoggers(jobID string) (logger.Interface, error) {
if lc.Name == logger.NameFile || lc.Name == logger.NameDB { if lc.Name == logger.NameFile || lc.Name == logger.NameDB {
// Need extra param // Need extra param
fSettings := map[string]any{} fSettings := map[string]any{}
for k, v := range lc.Settings { maps.Copy(fSettings, lc.Settings)
// Copy settings
fSettings[k] = v
}
if lc.Name == logger.NameFile { if lc.Name == logger.NameFile {
// Append file name param // Append file name param
fSettings["filename"] = fmt.Sprintf("%s.log", jobID) fSettings["filename"] = fmt.Sprintf("%s.log", jobID)

View File

@ -17,6 +17,7 @@ package impl
import ( import (
"context" "context"
"errors" "errors"
"maps"
o "github.com/beego/beego/v2/client/orm" o "github.com/beego/beego/v2/client/orm"
@ -61,9 +62,7 @@ func (dc *DefaultContext) Build(t job.Tracker) (job.Context, error) {
// Copy properties // Copy properties
if len(dc.properties) > 0 { if len(dc.properties) > 0 {
for k, v := range dc.properties { maps.Copy(jContext.properties, dc.properties)
jContext.properties[k] = v
}
} }
// Set loggers for job // Set loggers for job

View File

@ -299,10 +299,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
blobChunkCount := (total + blobChunkSize - 1) / blobChunkSize blobChunkCount := (total + blobChunkSize - 1) / blobChunkSize
blobChunks := make([][]*blobModels.Blob, blobChunkCount) blobChunks := make([][]*blobModels.Blob, blobChunkCount)
for i, start := 0, 0; i < blobChunkCount; i, start = i+1, start+blobChunkSize { for i, start := 0, 0; i < blobChunkCount; i, start = i+1, start+blobChunkSize {
end := start + blobChunkSize end := min(start+blobChunkSize, total)
if end > total {
end = total
}
blobChunks[i] = gc.deleteSet[start:end] blobChunks[i] = gc.deleteSet[start:end]
} }

View File

@ -59,7 +59,7 @@ func TestDelKeys(t *testing.T) {
// helper function // helper function
// mock the data in the redis // mock the data in the redis
mock := func(count int, prefix string) { mock := func(count int, prefix string) {
for i := 0; i < count; i++ { for i := range count {
err = c.Save(context.TODO(), fmt.Sprintf("%s-%d", prefix, i), "", 0) err = c.Save(context.TODO(), fmt.Sprintf("%s-%d", prefix, i), "", 0)
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@ -17,6 +17,7 @@ package scandataexport
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"maps"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -173,9 +174,8 @@ func (sde *ScanDataExport) updateExecAttributes(ctx job.Context, params job.Para
} }
// copy old extra // copy old extra
attrsToUpdate := exec.ExtraAttrs attrsToUpdate := exec.ExtraAttrs
for k, v := range attrs { maps.Copy(attrsToUpdate, attrs)
attrsToUpdate[k] = v
}
return sde.execMgr.UpdateExtraAttrs(ctx.SystemContext(), execID, attrsToUpdate) return sde.execMgr.UpdateExtraAttrs(ctx.SystemContext(), execID, attrsToUpdate)
} }

View File

@ -1,7 +1,6 @@
package logger package logger
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"path" "path"
@ -97,8 +96,7 @@ func TestGetLoggersMulti(t *testing.T) {
// Test getting sweepers // Test getting sweepers
func TestGetSweeper(t *testing.T) { func TestGetSweeper(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
_, err := GetSweeper(ctx) _, err := GetSweeper(ctx)
if err == nil { if err == nil {
@ -170,8 +168,7 @@ func TestGetGetter(t *testing.T) {
// Test init // Test init
func TestLoggerInit(t *testing.T) { func TestLoggerInit(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background()) ctx := t.Context()
defer cancel()
oldJobLoggerCfg := config.DefaultConfig.JobLoggerConfigs oldJobLoggerCfg := config.DefaultConfig.JobLoggerConfigs
oldLoggerCfg := config.DefaultConfig.LoggerConfigs oldLoggerCfg := config.DefaultConfig.LoggerConfigs

View File

@ -99,10 +99,8 @@ func tailLogFile(filename string, limit int64) ([]byte, error) {
} }
defer fi.Close() defer fi.Close()
pos := size - sizeToRead pos := max(size-sizeToRead, 0)
if pos < 0 {
pos = 0
}
if pos != 0 { if pos != 0 {
_, err = fi.Seek(pos, 0) _, err = fi.Seek(pos, 0)
if err != nil { if err != nil {

View File

@ -16,6 +16,7 @@ package logger
import ( import (
"reflect" "reflect"
"slices"
"strings" "strings"
"github.com/goharbor/harbor/src/jobservice/logger/backend" "github.com/goharbor/harbor/src/jobservice/logger/backend"
@ -89,13 +90,7 @@ func IsKnownLevel(level string) bool {
return false return false
} }
for _, lvl := range debugLevels { return slices.Contains(debugLevels, strings.ToUpper(level))
if lvl == strings.ToUpper(level) {
return true
}
}
return false
} }
// GetLoggerName return a logger name by Interface // GetLoggerName return a logger name by Interface

View File

@ -17,6 +17,7 @@ package period
import ( import (
"context" "context"
"fmt" "fmt"
"maps"
"math/rand" "math/rand"
"time" "time"
@ -303,9 +304,7 @@ func cloneParameters(params job.Parameters, epoch int64) job.Parameters {
p := make(job.Parameters) p := make(job.Parameters)
// Clone parameters to a new param map // Clone parameters to a new param map
for k, v := range params { maps.Copy(p, params)
p[k] = v
}
p[PeriodicExecutionMark] = fmt.Sprintf("%d", epoch) p[PeriodicExecutionMark] = fmt.Sprintf("%d", epoch)

View File

@ -19,7 +19,7 @@ import (
) )
func BenchmarkDefaultCodecEncode(b *testing.B) { func BenchmarkDefaultCodecEncode(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
codec.Encode("abcdefghigklmopqrztuvwxyz") codec.Encode("abcdefghigklmopqrztuvwxyz")
} }
} }
@ -27,7 +27,7 @@ func BenchmarkDefaultCodecEncode(b *testing.B) {
func BenchmarkDefaultCodecDecode(b *testing.B) { func BenchmarkDefaultCodecDecode(b *testing.B) {
data := []byte("abcdefghigklmopqrztuvwxyz") data := []byte("abcdefghigklmopqrztuvwxyz")
for i := 0; i < b.N; i++ { for b.Loop() {
var str string var str string
codec.Decode(data, &str) codec.Decode(data, &str)
} }

View File

@ -103,7 +103,7 @@ func (suite *FetchOrSaveTestSuite) TestSaveCalledOnlyOneTime() {
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 1000; i++ { for range 1000 {
wg.Add(1) wg.Add(1)
go func() { go func() {

View File

@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
func (suite *CacheTestSuite) TestScan() { func (suite *CacheTestSuite) TestScan() {
seed := func(n int) { seed := func(n int) {
for i := 0; i < n; i++ { for i := range n {
key := fmt.Sprintf("test-scan-%d", i) key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Save(suite.ctx, key, "") err := suite.cache.Save(suite.ctx, key, "")
suite.NoError(err) suite.NoError(err)
} }
} }
clean := func(n int) { clean := func(n int) {
for i := 0; i < n; i++ { for i := range n {
key := fmt.Sprintf("test-scan-%d", i) key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Delete(suite.ctx, key) err := suite.cache.Delete(suite.ctx, key)
suite.NoError(err) suite.NoError(err)

View File

@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
func (suite *CacheTestSuite) TestScan() { func (suite *CacheTestSuite) TestScan() {
seed := func(n int) { seed := func(n int) {
for i := 0; i < n; i++ { for i := range n {
key := fmt.Sprintf("test-scan-%d", i) key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Save(suite.ctx, key, "") err := suite.cache.Save(suite.ctx, key, "")
suite.NoError(err) suite.NoError(err)
} }
} }
clean := func(n int) { clean := func(n int) {
for i := 0; i < n; i++ { for i := range n {
key := fmt.Sprintf("test-scan-%d", i) key := fmt.Sprintf("test-scan-%d", i)
err := suite.cache.Delete(suite.ctx, key) err := suite.cache.Delete(suite.ctx, key)
suite.NoError(err) suite.NoError(err)

View File

@ -26,7 +26,7 @@ func TestKeyMutex(t *testing.T) {
key := "key" key := "key"
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Add(1)
go func() { go func() {

View File

@ -220,7 +220,7 @@ func RobotPrefix(ctx context.Context) string {
// SplitAndTrim ... // SplitAndTrim ...
func SplitAndTrim(s, sep string) []string { func SplitAndTrim(s, sep string) []string {
res := make([]string, 0) res := make([]string, 0)
for _, s := range strings.Split(s, sep) { for s := range strings.SplitSeq(s, sep) {
if e := strings.TrimSpace(s); len(e) > 0 { if e := strings.TrimSpace(s); len(e) > 0 {
res = append(res, e) res = append(res, e)
} }
@ -269,8 +269,7 @@ func AuditLogEventEnabled(ctx context.Context, eventType string) bool {
return true return true
} }
disableListStr := DefaultMgr().Get(ctx, common.AuditLogEventsDisabled).GetString() disableListStr := DefaultMgr().Get(ctx, common.AuditLogEventsDisabled).GetString()
disableList := strings.Split(disableListStr, ",") for t := range strings.SplitSeq(disableListStr, ",") {
for _, t := range disableList {
tName := strings.TrimSpace(t) tName := strings.TrimSpace(t)
if strings.EqualFold(tName, eventType) { if strings.EqualFold(tName, eventType) {
return false return false

View File

@ -27,7 +27,7 @@ func TestAddTask(t *testing.T) {
taskNum := 3 taskNum := 3
taskInterval := time.Duration(0) taskInterval := time.Duration(0)
for i := 0; i < taskNum; i++ { for i := range taskNum {
fn := func(ctx context.Context) { fn := func(ctx context.Context) {
t.Logf("Task %d is running...", i) t.Logf("Task %d is running...", i)
} }
@ -64,8 +64,7 @@ func TestStartAndStop(t *testing.T) {
pool.tasks = []*task{t1, t2} pool.tasks = []*task{t1, t2}
ctx1, cancel1 := context.WithCancel(context.Background()) ctx1 := t.Context()
defer cancel1()
pool.Start(ctx1) pool.Start(ctx1)
// Let it run for a bit // Let it run for a bit

View File

@ -54,11 +54,11 @@ func (l Links) String() string {
// e.g. <http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter" , <http://example.com/TheBook/chapter4>; rel="next"; title="next chapter" // e.g. <http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter" , <http://example.com/TheBook/chapter4>; rel="next"; title="next chapter"
func ParseLinks(str string) Links { func ParseLinks(str string) Links {
var links Links var links Links
for _, lk := range strings.Split(str, ",") { for lk := range strings.SplitSeq(str, ",") {
link := &Link{ link := &Link{
Attrs: map[string]string{}, Attrs: map[string]string{},
} }
for _, attr := range strings.Split(lk, ";") { for attr := range strings.SplitSeq(lk, ";") {
attr = strings.TrimSpace(attr) attr = strings.TrimSpace(attr)
if len(attr) == 0 { if len(attr) == 0 {
continue continue

View File

@ -17,6 +17,7 @@ package log
import ( import (
"fmt" "fmt"
"io" "io"
"maps"
"os" "os"
"runtime" "runtime"
"sort" "sort"
@ -122,12 +123,8 @@ func (l *Logger) WithFields(fields Fields) *Logger {
if len(fields) > 0 { if len(fields) > 0 {
copyFields := make(map[string]any, len(l.fields)+len(fields)) copyFields := make(map[string]any, len(l.fields)+len(fields))
for key, value := range l.fields { maps.Copy(copyFields, l.fields)
copyFields[key] = value maps.Copy(copyFields, fields)
}
for key, value := range fields {
copyFields[key] = value
}
sortedKeys := make([]string, 0, len(copyFields)) sortedKeys := make([]string, 0, len(copyFields))
for key := range copyFields { for key := range copyFields {

View File

@ -79,7 +79,7 @@ func parseModel(model any) *metadata {
Keys: map[string]*key{}, Keys: map[string]*key{},
} }
// parse fields of the provided model // parse fields of the provided model
for i := 0; i < t.NumField(); i++ { for i := range t.NumField() {
field := t.Field(i) field := t.Field(i)
orm := field.Tag.Get("orm") orm := field.Tag.Get("orm")
// isn't the database column, skip // isn't the database column, skip
@ -107,7 +107,7 @@ func parseModel(model any) *metadata {
} }
// parse filter methods of the provided model // parse filter methods of the provided model
for i := 0; i < ptr.NumMethod(); i++ { for i := range ptr.NumMethod() {
methodName := ptr.Method(i).Name methodName := ptr.Method(i).Name
if !strings.HasPrefix(methodName, "FilterBy") { if !strings.HasPrefix(methodName, "FilterBy") {
continue continue
@ -173,7 +173,7 @@ func parseFilterable(field reflect.StructField) bool {
// } // }
func parseSortable(field reflect.StructField) (*q.Sort, bool) { func parseSortable(field reflect.StructField) (*q.Sort, bool) {
var defaultSort *q.Sort var defaultSort *q.Sort
for _, item := range strings.Split(field.Tag.Get("sort"), ";") { for item := range strings.SplitSeq(field.Tag.Get("sort"), ";") {
// isn't sortable, return directly // isn't sortable, return directly
if item == "false" { if item == "false" {
return nil, false return nil, false
@ -202,7 +202,7 @@ func parseSortable(field reflect.StructField) (*q.Sort, bool) {
// It returns "customized_field1" for "Field1" and returns "field2" for "Field2" // It returns "customized_field1" for "Field1" and returns "field2" for "Field2"
func parseColumn(field reflect.StructField) string { func parseColumn(field reflect.StructField) string {
column := "" column := ""
for _, item := range strings.Split(field.Tag.Get("orm"), ";") { for item := range strings.SplitSeq(field.Tag.Get("orm"), ";") {
if !strings.HasPrefix(item, "column") { if !strings.HasPrefix(item, "column") {
continue continue
} }
@ -224,7 +224,7 @@ func snakeCase(str string) string {
runes := []rune(str) runes := []rune(str)
var out []rune var out []rune
for i := 0; i < len(runes); i++ { for i := range len(runes) {
if i > 0 && if i > 0 &&
(unicode.IsUpper(runes[i])) && (unicode.IsUpper(runes[i])) &&
((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) { ((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {

View File

@ -271,7 +271,7 @@ func Escape(str string) string {
// e.g. n=3, returns "?,?,?" // e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string { func ParamPlaceholderForIn(n int) string {
placeholders := []string{} placeholders := []string{}
for i := 0; i < n; i++ { for range n {
placeholders = append(placeholders, "?") placeholders = append(placeholders, "?")
} }
return strings.Join(placeholders, ",") return strings.Join(placeholders, ",")

View File

@ -387,7 +387,7 @@ func (suite *OrmSuite) TestReadOrCreateParallel() {
arr := make([]int, count) arr := make([]int, count)
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < count; i++ { for i := range count {
wg.Add(1) wg.Add(1)
go func(i int) { go func(i int) {
defer wg.Done() defer wg.Done()

View File

@ -58,8 +58,8 @@ func parseKeywords(q string) (map[string]any, error) {
} else { } else {
log.Errorf("failed to unescape the query %s: %v", q, err) log.Errorf("failed to unescape the query %s: %v", q, err)
} }
params := strings.Split(q, ",")
for _, param := range params { for param := range strings.SplitSeq(q, ",") {
strs := strings.SplitN(param, "=", 2) strs := strings.SplitN(param, "=", 2)
if len(strs) != 2 || len(strs[0]) == 0 || len(strs[1]) == 0 { if len(strs) != 2 || len(strs[0]) == 0 || len(strs[1]) == 0 {
return nil, errors.New(nil). return nil, errors.New(nil).
@ -82,7 +82,7 @@ func ParseSorting(sort string) []*Sort {
return []*Sort{} return []*Sort{}
} }
var sorts []*Sort var sorts []*Sort
for _, sorting := range strings.Split(sort, ",") { for sorting := range strings.SplitSeq(sort, ",") {
key := sorting key := sorting
desc := false desc := false
if strings.HasPrefix(sorting, "-") { if strings.HasPrefix(sorting, "-") {
@ -176,8 +176,8 @@ func parseList(value string, c rune) ([]any, error) {
return nil, fmt.Errorf(`and list must start with "(" and end with ")"`) return nil, fmt.Errorf(`and list must start with "(" and end with ")"`)
} }
var vs []any var vs []any
strs := strings.Split(value[1:length-1], " ")
for _, str := range strs { for str := range strings.SplitSeq(value[1:length-1], " ") {
v := parseValue(str) v := parseValue(str)
if s, ok := v.(string); ok && len(s) == 0 { if s, ok := v.(string); ok && len(s) == 0 {
continue continue

View File

@ -14,6 +14,8 @@
package q package q
import "maps"
// KeyWords ... // KeyWords ...
type KeyWords = map[string]any type KeyWords = map[string]any
@ -56,9 +58,8 @@ func MustClone(query *Query) *Query {
if query != nil { if query != nil {
q.PageNumber = query.PageNumber q.PageNumber = query.PageNumber
q.PageSize = query.PageSize q.PageSize = query.PageSize
for k, v := range query.Keywords { maps.Copy(q.Keywords, query.Keywords)
q.Keywords[k] = v
}
for _, sort := range query.Sorts { for _, sort := range query.Sorts {
q.Sorts = append(q.Sorts, &Sort{ q.Sorts = append(q.Sorts, &Sort{
Key: sort.Key, Key: sort.Key,

View File

@ -34,7 +34,7 @@ func TestGetRegistryClient(t *testing.T) {
assert.NotNil(t, client) assert.NotNil(t, client)
// multiple calls should return the same client // multiple calls should return the same client
for i := 0; i < 10; i++ { for range 10 {
newClient, err := GetRegistryClient() newClient, err := GetRegistryClient()
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, client, newClient) assert.Equal(t, client, newClient)
@ -55,7 +55,7 @@ func TestGetHarborClient(t *testing.T) {
assert.NotNil(t, client) assert.NotNil(t, client)
// multiple calls should return the same client // multiple calls should return the same client
for i := 0; i < 10; i++ { for range 10 {
newClient, err := GetHarborClient() newClient, err := GetHarborClient()
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, client, newClient) assert.Equal(t, client, newClient)

View File

@ -15,6 +15,7 @@
package index package index
import ( import (
"slices"
"sync" "sync"
"github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/errors"
@ -80,11 +81,9 @@ func Get(kind, decoration, pattern, extras string) (selector.Selector, error) {
} }
item := v.(*indexedItem) item := v.(*indexedItem)
for _, dec := range item.Meta.Decorations { if slices.Contains(item.Meta.Decorations, decoration) {
if dec == decoration { factory := item.Factory
factory := item.Factory return factory(decoration, pattern, extras), nil
return factory(decoration, pattern, extras), nil
}
} }
return nil, errors.Errorf("decoration %s of selector %s is not supported", decoration, kind) return nil, errors.Errorf("decoration %s of selector %s is not supported", decoration, kind)

View File

@ -28,7 +28,7 @@ func (s *testSuite) TestSetAndGet() {
s.Nil(err) s.Nil(err)
s.Nil(l) s.Nil(l)
var longList []models.CVEAllowlistItem var longList []models.CVEAllowlistItem
for i := 0; i < 50; i++ { for range 50 {
longList = append(longList, models.CVEAllowlistItem{CVEID: "CVE-1999-0067"}) longList = append(longList, models.CVEAllowlistItem{CVEID: "CVE-1999-0067"})
} }

View File

@ -16,6 +16,7 @@ package allowlist
import ( import (
"fmt" "fmt"
"strings"
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models" models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
) )
@ -45,6 +46,13 @@ func IsInvalidErr(err error) bool {
func Validate(wl models2.CVEAllowlist) error { func Validate(wl models2.CVEAllowlist) error {
m := map[string]struct{}{} m := map[string]struct{}{}
for _, it := range wl.Items { for _, it := range wl.Items {
cveID := strings.TrimSpace(it.CVEID)
// Check for empty or whitespace-only CVE IDs
if cveID == "" {
return &invalidErr{fmt.Sprintf("empty or whitespace-only CVE ID in allowlist")}
}
// Check for duplicates
if _, ok := m[it.CVEID]; ok { if _, ok := m[it.CVEID]; ok {
return &invalidErr{fmt.Sprintf("duplicate CVE ID in allowlist: %s", it.CVEID)} return &invalidErr{fmt.Sprintf("duplicate CVE ID in allowlist: %s", it.CVEID)}
} }

View File

@ -53,6 +53,22 @@ func TestValidate(t *testing.T) {
l models2.CVEAllowlist l models2.CVEAllowlist
noError bool noError bool
}{ }{
{
l: models2.CVEAllowlist{
Items: []models2.CVEAllowlistItem{
{CVEID: ""},
},
},
noError: false,
},
{
l: models2.CVEAllowlist{
Items: []models2.CVEAllowlistItem{
{CVEID: " "},
},
},
noError: false,
},
{ {
l: models2.CVEAllowlist{ l: models2.CVEAllowlist{
Items: nil, Items: nil,

View File

@ -16,11 +16,11 @@ package dao
import ( import (
"context" "context"
"slices"
"strings" "strings"
beegorm "github.com/beego/beego/v2/client/orm" beegorm "github.com/beego/beego/v2/client/orm"
"github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/errors"
"github.com/goharbor/harbor/src/lib/log" "github.com/goharbor/harbor/src/lib/log"
"github.com/goharbor/harbor/src/lib/orm" "github.com/goharbor/harbor/src/lib/orm"
@ -197,7 +197,7 @@ func permitEventTypes(includeEventTypes []string) []string {
var filterEvents []string var filterEvents []string
for _, e := range includeEventTypes { for _, e := range includeEventTypes {
event := strings.ToLower(e) event := strings.ToLower(e)
if utils.StringInSlice(event, model.EventTypes) { if slices.Contains(model.EventTypes, event) {
filterEvents = append(filterEvents, e) filterEvents = append(filterEvents, e)
} else if event == model.OtherEvents { // include all other events } else if event == model.OtherEvents { // include all other events
filterEvents = append(filterEvents, model.OtherEventTypes...) filterEvents = append(filterEvents, model.OtherEventTypes...)

View File

@ -75,7 +75,7 @@ func (ok *ObjectKey) Format(keysAndValues ...any) (string, error) {
} }
s := ok.namespace s := ok.namespace
for i := 0; i < len(keysAndValues); i++ { for i := range len(keysAndValues) {
// even is key // even is key
if i%2 == 0 { if i%2 == 0 {
key, match := keysAndValues[i].(string) key, match := keysAndValues[i].(string)

View File

@ -16,6 +16,7 @@ package inmemory
import ( import (
"context" "context"
"maps"
"sync" "sync"
"github.com/goharbor/harbor/src/common" "github.com/goharbor/harbor/src/common"
@ -41,9 +42,7 @@ func (d *Driver) Load(context.Context) (map[string]any, error) {
d.Lock() d.Lock()
defer d.Unlock() defer d.Unlock()
res := make(map[string]any) res := make(map[string]any)
for k, v := range d.cfgMap { maps.Copy(res, d.cfgMap)
res[k] = v
}
return res, nil return res, nil
} }
@ -51,9 +50,7 @@ func (d *Driver) Load(context.Context) (map[string]any, error) {
func (d *Driver) Save(_ context.Context, cfg map[string]any) error { func (d *Driver) Save(_ context.Context, cfg map[string]any) error {
d.Lock() d.Lock()
defer d.Unlock() defer d.Unlock()
for k, v := range cfg { maps.Copy(d.cfgMap, cfg)
d.cfgMap[k] = v
}
return nil return nil
} }

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"io" "io"
"maps"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"testing" "testing"
@ -66,9 +67,7 @@ func ConfigPutHandler(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
for k, v := range cfgs { maps.Copy(configMapForTest, cfgs)
configMapForTest[k] = v
}
} }
func TestHTTPDriver_Save(t *testing.T) { func TestHTTPDriver_Save(t *testing.T) {

View File

@ -88,10 +88,7 @@ func (r *redisClientImpl) StopPendingJobs(ctx context.Context, jobType string) (
// use batch to list the job in queue, because the too many object load from a list might cause the redis crash // use batch to list the job in queue, because the too many object load from a list might cause the redis crash
for startIndex := int64(0); startIndex < int64(size); startIndex += batchSize { for startIndex := int64(0); startIndex < int64(size); startIndex += batchSize {
endIndex := startIndex + batchSize endIndex := min(startIndex+batchSize, int64(size))
if endIndex > int64(size) {
endIndex = int64(size)
}
jobs, err := redis.Strings(conn.Do("LRANGE", redisKeyJobQueue, startIndex, endIndex)) jobs, err := redis.Strings(conn.Do("LRANGE", redisKeyJobQueue, startIndex, endIndex))
if err != nil { if err != nil {
return []string{}, err return []string{}, err

View File

@ -59,7 +59,7 @@ func (s *RedisClientTestSuite) TestUntrackJobStatusInBatch() {
jobIDs := make([]string, 0) jobIDs := make([]string, 0)
conn := s.redisClient.redisPool.Get() conn := s.redisClient.redisPool.Get()
defer conn.Close() defer conn.Close()
for i := 0; i < 100; i++ { for range 100 {
k := utils.GenerateRandomStringWithLen(10) k := utils.GenerateRandomStringWithLen(10)
jobIDs = append(jobIDs, k) jobIDs = append(jobIDs, k)
key := rds.KeyJobStats(fmt.Sprintf("{%s}", s.redisClient.namespace), k) key := rds.KeyJobStats(fmt.Sprintf("{%s}", s.redisClient.namespace), k)
@ -92,7 +92,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
} }
conn := s.redisClient.redisPool.Get() conn := s.redisClient.redisPool.Get()
defer conn.Close() defer conn.Close()
for i := 0; i < 100; i++ { for range 100 {
job := jobInfo{ job := jobInfo{
ID: utils.GenerateRandomStringWithLen(10), ID: utils.GenerateRandomStringWithLen(10),
Params: utils.GenerateRandomStringWithLen(10), Params: utils.GenerateRandomStringWithLen(10),
@ -107,7 +107,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
} }
} }
// job without id // job without id
for i := 0; i < 10; i++ { for range 10 {
job := jobInfo{ job := jobInfo{
Params: utils.GenerateRandomStringWithLen(10), Params: utils.GenerateRandomStringWithLen(10),
} }

View File

@ -173,7 +173,7 @@ func TestConcurrentPublish(t *testing.T) {
} }
// Publish in a short interval. // Publish in a short interval.
for i := 0; i < 10; i++ { for range 10 {
Publish(context.TODO(), "topic1", 100) Publish(context.TODO(), "topic1", 100)
} }

View File

@ -22,6 +22,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
"slices"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -392,11 +393,8 @@ func userInfoFromClaims(c claimsProvider, setting cfgModels.OIDCSetting) (*UserI
} }
res.Groups, res.hasGroupClaim = groupsFromClaims(c, setting.GroupsClaim) res.Groups, res.hasGroupClaim = groupsFromClaims(c, setting.GroupsClaim)
if len(setting.AdminGroup) > 0 { if len(setting.AdminGroup) > 0 {
for _, g := range res.Groups { if slices.Contains(res.Groups, setting.AdminGroup) {
if g == setting.AdminGroup { res.AdminGroupMember = true
res.AdminGroupMember = true
break
}
} }
} }
return res, nil return res, nil

View File

@ -36,13 +36,13 @@ func TestExpiration(t *testing.T) {
func TestGC(t *testing.T) { func TestGC(t *testing.T) {
manager := createManager(1*time.Second, 10, 1*time.Second).(*mgr) manager := createManager(1*time.Second, 10, 1*time.Second).(*mgr)
for i := 0; i < 10; i++ { for i := range 10 {
rn := fmt.Sprintf("project%d/golang", i) rn := fmt.Sprintf("project%d/golang", i)
manager.Generate(rn) manager.Generate(rn)
} }
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
assert.Equal(t, uint64(10), manager.size) assert.Equal(t, uint64(10), manager.size)
for i := 0; i < 1000; i++ { for i := range 1000 {
rn := fmt.Sprintf("project%d/redis", i) rn := fmt.Sprintf("project%d/redis", i)
manager.Generate(rn) manager.Generate(rn)
} }

View File

@ -329,7 +329,7 @@ func compileRegexpEveryTime(url string) (string, string, error) {
} }
func BenchmarkGetAccountRegion(b *testing.B) { func BenchmarkGetAccountRegion(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
for _, url := range urlForBenchmark { for _, url := range urlForBenchmark {
parseAccountRegion(url) parseAccountRegion(url)
} }
@ -337,7 +337,7 @@ func BenchmarkGetAccountRegion(b *testing.B) {
} }
func BenchmarkCompileRegexpEveryTime(b *testing.B) { func BenchmarkCompileRegexpEveryTime(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
for _, url := range urlForBenchmark { for _, url := range urlForBenchmark {
compileRegexpEveryTime(url) compileRegexpEveryTime(url)
} }

View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"slices"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
@ -195,10 +196,8 @@ func (a adapter) listGcrTagsByRef(repository, reference string) ([]string, strin
} }
// for tag as reference // for tag as reference
for d, m := range tgs.Manifest { for d, m := range tgs.Manifest {
for _, t := range m.Tag { if slices.Contains(m.Tag, reference) {
if t == reference { return m.Tag, d, nil
return m.Tag, d, nil
}
} }
} }
return nil, "", nil return nil, "", nil

View File

@ -40,7 +40,7 @@ func IsSpecificPath(path string) ([]string, bool) {
return nil, false return nil, false
} }
components := [][]string{} components := [][]string{}
for _, component := range strings.Split(path, "/") { for component := range strings.SplitSeq(path, "/") {
strs, ok := IsSpecificPathComponent(component) strs, ok := IsSpecificPathComponent(component)
if !ok { if !ok {
return nil, false return nil, false
@ -113,8 +113,7 @@ func IsSpecificPathComponent(component string) ([]string, bool) {
suffix = component[j+1:] suffix = component[j+1:]
} }
components := []string{} components := []string{}
strs := strings.Split(component[i+1:j], ",") for str := range strings.SplitSeq(component[i+1:j], ",") {
for _, str := range strs {
components = append(components, prefix+str+suffix) components = append(components, prefix+str+suffix)
} }
return components, true return components, true

View File

@ -49,11 +49,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return activeTime(artifacts[i]) > activeTime(artifacts[j]) return activeTime(artifacts[i]) > activeTime(artifacts[j])
}) })
i := e.k i := min(e.k, len(artifacts))
if i > len(artifacts) {
i = len(artifacts)
}
return artifacts[:i], nil return artifacts[:i], nil
} }

View File

@ -46,11 +46,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return artifacts[i].PulledTime > artifacts[j].PulledTime return artifacts[i].PulledTime > artifacts[j].PulledTime
}) })
i := e.n i := min(e.n, len(artifacts))
if i > len(artifacts) {
i = len(artifacts)
}
return artifacts[:i], nil return artifacts[:i], nil
} }

View File

@ -48,11 +48,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
return artifacts[i].PushedTime > artifacts[j].PushedTime return artifacts[i].PushedTime > artifacts[j].PushedTime
}) })
i := e.k i := min(e.k, len(artifacts))
if i > len(artifacts) {
i = len(artifacts)
}
return artifacts[:i], nil return artifacts[:i], nil
} }

View File

@ -16,6 +16,7 @@ package scanner
import ( import (
"encoding/json" "encoding/json"
"slices"
"time" "time"
"github.com/goharbor/harbor/src/lib" "github.com/goharbor/harbor/src/lib"
@ -140,15 +141,9 @@ func (r *Registration) HasCapability(manifestMimeType string) bool {
return false return false
} }
for _, capability := range r.Metadata.Capabilities { return slices.ContainsFunc(r.Metadata.Capabilities, func(c *v1.ScannerCapability) bool {
for _, mt := range capability.ConsumesMimeTypes { return slices.Contains(c.ConsumesMimeTypes, manifestMimeType)
if mt == manifestMimeType { })
return true
}
}
}
return false
} }
// GetProducesMimeTypes returns produces mime types for the artifact // GetProducesMimeTypes returns produces mime types for the artifact
@ -162,10 +157,8 @@ func (r *Registration) GetProducesMimeTypes(mimeType string, scanType string) []
capType = v1.ScanTypeVulnerability capType = v1.ScanTypeVulnerability
} }
if scanType == capType { if scanType == capType {
for _, mt := range capability.ConsumesMimeTypes { if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
if mt == mimeType { return capability.ProducesMimeTypes
return capability.ProducesMimeTypes
}
} }
} }
} }
@ -180,10 +173,8 @@ func (r *Registration) GetCapability(mimeType string) *v1.ScannerCapability {
} }
for _, capability := range r.Metadata.Capabilities { for _, capability := range r.Metadata.Capabilities {
for _, mt := range capability.ConsumesMimeTypes { if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
if mt == mimeType { return capability
return capability
}
} }
} }

View File

@ -16,6 +16,7 @@ package export
import ( import (
"context" "context"
"slices"
"github.com/goharbor/harbor/src/common/utils" "github.com/goharbor/harbor/src/common/utils"
"github.com/goharbor/harbor/src/controller/artifact" "github.com/goharbor/harbor/src/controller/artifact"
@ -170,10 +171,8 @@ func (dfp *DefaultFilterProcessor) ProcessLabelFilter(_ context.Context, labelID
// TODO (as now there should not have many labels, so here just use // TODO (as now there should not have many labels, so here just use
// for^2, we can convert to use map to reduce the time complex if needed. ) // for^2, we can convert to use map to reduce the time complex if needed. )
for _, label := range art.Labels { for _, label := range art.Labels {
for _, labelID := range labelIDs { if slices.Contains(labelIDs, label.ID) {
if labelID == label.ID { return true
return true
}
} }
} }
return false return false

View File

@ -101,10 +101,7 @@ func GenerateNativeSummary(r *scan.Report, _ ...Option) (any, error) {
sum.ReportID = r.UUID sum.ReportID = r.UUID
sum.StartTime = r.StartTime sum.StartTime = r.StartTime
sum.EndTime = r.EndTime sum.EndTime = r.EndTime
sum.Duration = r.EndTime.Unix() - r.StartTime.Unix() sum.Duration = max(r.EndTime.Unix()-r.StartTime.Unix(), 0)
if sum.Duration < 0 {
sum.Duration = 0
}
sum.ScanStatus = job.ErrorStatus.String() sum.ScanStatus = job.ErrorStatus.String()
if job.Status(r.Status).Code() != -1 { if job.Status(r.Status).Code() != -1 {

View File

@ -17,6 +17,7 @@ package v1
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices"
"github.com/goharbor/harbor/src/lib/errors" "github.com/goharbor/harbor/src/lib/errors"
) )
@ -95,26 +96,15 @@ func (md *ScannerAdapterMetadata) Validate() error {
for _, ca := range md.Capabilities { for _, ca := range md.Capabilities {
// v1.MimeTypeDockerArtifact is required now // v1.MimeTypeDockerArtifact is required now
found := false found := slices.Contains(ca.ConsumesMimeTypes, MimeTypeDockerArtifact)
for _, cm := range ca.ConsumesMimeTypes {
if cm == MimeTypeDockerArtifact {
found = true
break
}
}
if !found { if !found {
return errors.Errorf("missing %s in consumes_mime_types", MimeTypeDockerArtifact) return errors.Errorf("missing %s in consumes_mime_types", MimeTypeDockerArtifact)
} }
// either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required // either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required
found = false found = slices.ContainsFunc(ca.ProducesMimeTypes, func(pm string) bool {
for _, pm := range ca.ProducesMimeTypes { return isSupportedMimeType(pm)
if isSupportedMimeType(pm) { })
found = true
break
}
}
if !found { if !found {
return errors.Errorf("missing %s or %s in produces_mime_types", MimeTypeNativeReport, MimeTypeGenericVulnerabilityReport) return errors.Errorf("missing %s or %s in produces_mime_types", MimeTypeNativeReport, MimeTypeGenericVulnerabilityReport)
} }
@ -124,34 +114,21 @@ func (md *ScannerAdapterMetadata) Validate() error {
} }
func isSupportedMimeType(mimeType string) bool { func isSupportedMimeType(mimeType string) bool {
for _, mt := range supportedMimeTypes { return slices.Contains(supportedMimeTypes, mimeType)
if mt == mimeType {
return true
}
}
return false
} }
// HasCapability returns true when mine type of the artifact support by the scanner // HasCapability returns true when mine type of the artifact support by the scanner
func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool { func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool {
for _, capability := range md.Capabilities { return slices.ContainsFunc(md.Capabilities, func(c *ScannerCapability) bool {
for _, mt := range capability.ConsumesMimeTypes { return slices.Contains(c.ConsumesMimeTypes, mimeType)
if mt == mimeType { })
return true
}
}
}
return false
} }
// GetCapability returns capability for the mime type // GetCapability returns capability for the mime type
func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapability { func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapability {
for _, capability := range md.Capabilities { for _, capability := range md.Capabilities {
for _, mt := range capability.ConsumesMimeTypes { if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
if mt == mimeType { return capability
return capability
}
} }
} }

View File

@ -18,6 +18,7 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"maps"
"sync" "sync"
"time" "time"
@ -206,9 +207,7 @@ func (mgr *systemArtifactManager) Cleanup(ctx context.Context) (int64, int64, er
// iterate through this copy to invoke the cleanup // iterate through this copy to invoke the cleanup
registeredCriteria := make(map[string]Selector, 0) registeredCriteria := make(map[string]Selector, 0)
mgr.lock.Lock() mgr.lock.Lock()
for key, val := range mgr.cleanupCriteria { maps.Copy(registeredCriteria, mgr.cleanupCriteria)
registeredCriteria[key] = val
}
mgr.lock.Unlock() mgr.lock.Unlock()
for key, val := range registeredCriteria { for key, val := range registeredCriteria {

View File

@ -224,7 +224,7 @@ func (e *executionDAO) GetMetrics(ctx context.Context, id int64) (*Metrics, erro
func (e *executionDAO) RefreshStatus(ctx context.Context, id int64) (bool, string, error) { func (e *executionDAO) RefreshStatus(ctx context.Context, id int64) (bool, string, error) {
// as the status of the execution can be refreshed by multiple operators concurrently // as the status of the execution can be refreshed by multiple operators concurrently
// we use the optimistic locking to avoid the conflict and retry 5 times at most // we use the optimistic locking to avoid the conflict and retry 5 times at most
for i := 0; i < 5; i++ { for range 5 {
statusChanged, currentStatus, retry, err := e.refreshStatus(ctx, id) statusChanged, currentStatus, retry, err := e.refreshStatus(ctx, id)
if err != nil { if err != nil {
return false, "", err return false, "", err

View File

@ -256,7 +256,7 @@ func (t *taskDAOTestSuite) TestGetMaxEndTime() {
func (t *taskDAOTestSuite) TestUpdateStatusInBatch() { func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
jobIDs := make([]string, 0) jobIDs := make([]string, 0)
taskIDs := make([]int64, 0) taskIDs := make([]int64, 0)
for i := 0; i < 300; i++ { for i := range 300 {
jobID := fmt.Sprintf("job-%d", i) jobID := fmt.Sprintf("job-%d", i)
tid, err := t.taskDAO.Create(t.ctx, &Task{ tid, err := t.taskDAO.Create(t.ctx, &Task{
JobID: jobID, JobID: jobID,
@ -272,7 +272,7 @@ func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
err := t.taskDAO.UpdateStatusInBatch(t.ctx, jobIDs, "Stopped", 10) err := t.taskDAO.UpdateStatusInBatch(t.ctx, jobIDs, "Stopped", 10)
t.Require().Nil(err) t.Require().Nil(err)
for i := 0; i < 300; i++ { for i := range 300 {
tasks, err := t.taskDAO.List(t.ctx, &q.Query{ tasks, err := t.taskDAO.List(t.ctx, &q.Query{
Keywords: q.KeyWords{"job_id": jobIDs[i]}}) Keywords: q.KeyWords{"job_id": jobIDs[i]}})
t.Require().Nil(err) t.Require().Nil(err)

View File

@ -165,11 +165,7 @@ func (sj *SweepJob) sweep(ctx job.Context, vendorType string, retainCount int64)
return errStop return errStop
} }
// calculate the batch position // calculate the batch position
j := i + sweepBatchSize j := min(i+sweepBatchSize, total)
// avoid overflow
if j > total {
j = total
}
if err = sj.mgr.Clean(ctx.SystemContext(), candidates[i:j]); err != nil { if err = sj.mgr.Clean(ctx.SystemContext(), candidates[i:j]); err != nil {
sj.logger.Errorf("[%s] failed to batch clean candidates, error: %v", vendorType, err) sj.logger.Errorf("[%s] failed to batch clean candidates, error: %v", vendorType, err)

View File

@ -99,4 +99,38 @@ describe('SecurityComponent', () => {
'CVE-2019-789' 'CVE-2019-789'
); );
}); });
it('should not allow empty and whitespace CVEs', async () => {
// set cveIds with mix empty and whitespace
component.cveIds = `
, , \n , \t, ,
`;
component.addToSystemAllowlist();
const finalIds = component.systemAllowlist.items.map(i => i.cve_id);
expect(finalIds).not.toContain(' ');
expect(finalIds).not.toContain('\n');
expect(finalIds).not.toContain(''); // no empty CVEs
// modal should be closed
expect(component.cveIds).toBeNull();
expect(component.showAddModal).toBeFalse();
});
it('should add only unique CVEs to the allowlist', () => {
// set cveIds with duplicates and valid
component.cveIds = `
CVE-2024-0002,
CVE-2024-0002,
CVE-2024-0004
`;
component.addToSystemAllowlist();
const finalIds = component.systemAllowlist.items.map(i => i.cve_id);
expect(finalIds).toContain('CVE-2024-0004');
expect(finalIds).not.toContain(''); // no empty CVEs
expect(finalIds.filter(id => id === 'CVE-2024-0002').length).toBe(1); // no duplicates
// modal should be closed
expect(component.cveIds).toBeNull();
expect(component.showAddModal).toBeFalse();
});
}); });

View File

@ -174,7 +174,12 @@ export class SecurityComponent implements OnInit, OnDestroy {
this.systemAllowlist.items.forEach(item => { this.systemAllowlist.items.forEach(item => {
map[item.cve_id] = true; map[item.cve_id] = true;
}); });
this.cveIds.split(/[\n,]+/).forEach(id => { const newCveIds = this.cveIds
.split(/[\n,]+/)
.map(id => id.trim()) // remove leading/trailing whitespace
.filter(id => id.length > 0); // skip empty or whitespace-only strings
newCveIds.forEach(id => {
let cveObj: any = {}; let cveObj: any = {};
cveObj.cve_id = id.trim(); cveObj.cve_id = id.trim();
if (!map[cveObj.cve_id]) { if (!map[cveObj.cve_id]) {

View File

@ -169,6 +169,46 @@ describe('ProjectPolicyConfigComponent', () => {
.GenerateSbomOnPush .GenerateSbomOnPush
).toBeTruthy(); ).toBeTruthy();
}); });
it('should not allow empty and whitespace CVEs', async () => {
// set cveIds with mix of empty and whitespace
component.projectPolicyConfigComponent.cveIds = `
, , \n , \t, ,
`;
component.projectPolicyConfigComponent.addToProjectAllowlist();
const finalIds =
component.projectPolicyConfigComponent.projectAllowlist.items.map(
i => i.cve_id
);
expect(finalIds).not.toContain(' ');
expect(finalIds).not.toContain('\n');
expect(finalIds).not.toContain(''); // no empty CVEs
// modal should be closed
expect(component.projectPolicyConfigComponent.cveIds).toBeNull();
expect(component.projectPolicyConfigComponent.showAddModal).toBeFalse();
});
it('should add only unique CVEs to the allowlist', () => {
// set cveIds with duplicates and valid
component.projectPolicyConfigComponent.cveIds = `
CVE-2024-0002,
CVE-2024-0002,
CVE-2024-0004
`;
component.projectPolicyConfigComponent.addToProjectAllowlist();
const finalIds =
component.projectPolicyConfigComponent.projectAllowlist.items.map(
i => i.cve_id
);
expect(finalIds).toContain('CVE-2024-0004');
expect(finalIds).not.toContain(''); // no empty CVEs
expect(finalIds.filter(id => id === 'CVE-2024-0002').length).toBe(1); // no duplicates
// modal should be closed
expect(component.projectPolicyConfigComponent.cveIds).toBeNull();
expect(component.projectPolicyConfigComponent.showAddModal).toBeFalse();
});
it('should get hasChangeConfigRole', () => { it('should get hasChangeConfigRole', () => {
expect( expect(
component.projectPolicyConfigComponent.hasChangeConfigRole component.projectPolicyConfigComponent.hasChangeConfigRole

View File

@ -13,7 +13,10 @@
// limitations under the License. // limitations under the License.
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
import { compareValue, clone } from '../../../../shared/units/utils'; import { compareValue, clone } from '../../../../shared/units/utils';
import { ProjectService } from '../../../../shared/services'; import {
ProjectCVEAllowlist,
ProjectService,
} from '../../../../shared/services';
import { ErrorHandler } from '../../../../shared/units/error-handler'; import { ErrorHandler } from '../../../../shared/units/error-handler';
import { State, SystemCVEAllowlist } from '../../../../shared/services'; import { State, SystemCVEAllowlist } from '../../../../shared/services';
import { import {
@ -133,8 +136,8 @@ export class ProjectPolicyConfigComponent implements OnInit {
userProjectAllowlist = false; userProjectAllowlist = false;
systemAllowlistOrProjectAllowlist: string; systemAllowlistOrProjectAllowlist: string;
systemAllowlistOrProjectAllowlistOrigin: string; systemAllowlistOrProjectAllowlistOrigin: string;
projectAllowlist; projectAllowlist: ProjectCVEAllowlist;
projectAllowlistOrigin; projectAllowlistOrigin: ProjectCVEAllowlist;
speedUnit = BandwidthUnit.KB; speedUnit = BandwidthUnit.KB;
speedUnits = [ speedUnits = [
{ {
@ -454,7 +457,12 @@ export class ProjectPolicyConfigComponent implements OnInit {
this.projectAllowlist.items.forEach(item => { this.projectAllowlist.items.forEach(item => {
map[item.cve_id] = true; map[item.cve_id] = true;
}); });
this.cveIds.split(/[\n,]+/).forEach(id => { const newCveIds = this.cveIds
.split(/[\n,]+/)
.map(id => id.trim()) // remove leading/trailing whitespace
.filter(id => id.length > 0); // skip empty or whitespace-only strings
newCveIds.forEach(id => {
let cveObj: any = {}; let cveObj: any = {};
cveObj.cve_id = id.trim(); cveObj.cve_id = id.trim();
if (!map[cveObj.cve_id]) { if (!map[cveObj.cve_id]) {

View File

@ -433,6 +433,11 @@ export interface ProjectRootInterface {
VALUE: number; VALUE: number;
LABEL: string; LABEL: string;
} }
export interface ProjectCVEAllowlist {
id?: number;
expires_at?: number;
items?: Array<{ cve_id: string }>;
}
export interface SystemCVEAllowlist { export interface SystemCVEAllowlist {
id?: number; id?: number;
project_id?: number; project_id?: number;

View File

@ -329,7 +329,7 @@ func proxyManifestHead(ctx context.Context, w http.ResponseWriter, ctl proxy.Con
// Then GET the image by digest, in order to associate the tag with the digest // Then GET the image by digest, in order to associate the tag with the digest
// Ensure tag after head request, make sure tags in proxy cache keep update // Ensure tag after head request, make sure tags in proxy cache keep update
bCtx := orm.Context() bCtx := orm.Context()
for i := 0; i < ensureTagMaxRetry; i++ { for range ensureTagMaxRetry {
time.Sleep(ensureTagInterval) time.Sleep(ensureTagInterval)
bArt := lib.ArtifactInfo{ProjectName: art.ProjectName, Repository: art.Repository, Digest: string(desc.Digest)} bArt := lib.ArtifactInfo{ProjectName: art.ProjectName, Repository: art.Repository, Digest: string(desc.Digest)}
err := ctl.EnsureTag(bCtx, bArt, art.Tag) err := ctl.EnsureTag(bCtx, bArt, art.Tag)

View File

@ -17,6 +17,7 @@ package vulnerable
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"slices"
"github.com/goharbor/harbor/src/controller/artifact/processor/cnab" "github.com/goharbor/harbor/src/controller/artifact/processor/cnab"
"github.com/goharbor/harbor/src/controller/artifact/processor/image" "github.com/goharbor/harbor/src/controller/artifact/processor/image"
@ -110,12 +111,10 @@ func Middleware() func(http.Handler) http.Handler {
if art.IsImageIndex() { if art.IsImageIndex() {
// artifact is image index, skip the checking when it is in the allowlist // artifact is image index, skip the checking when it is in the allowlist
skippingAllowlist := []string{image.ArtifactTypeImage, cnab.ArtifactTypeCNAB} skippingAllowlist := []string{image.ArtifactTypeImage, cnab.ArtifactTypeCNAB}
for _, t := range skippingAllowlist { if slices.Contains(skippingAllowlist, art.Type) {
if art.Type == t { logger.Debugf("artifact %s@%s is image index and its type is %s in skipping allowlist, "+
logger.Debugf("artifact %s@%s is image index and its type is %s in skipping allowlist, "+ "skip the vulnerability prevention checking", art.RepositoryName, art.Digest, art.Type)
"skip the vulnerability prevention checking", art.RepositoryName, art.Digest, art.Type) return nil
return nil
}
} }
} }

View File

@ -147,12 +147,7 @@ func pickItems(items []string, n *int, last string) ([]string, string) {
i = sort.Search(len(items), func(ix int) bool { return strings.Compare(items[ix], last) > 0 }) i = sort.Search(len(items), func(ix int) bool { return strings.Compare(items[ix], last) > 0 })
} }
j := i + *n j := min(i+*n, len(items))
if j >= len(items) {
j = len(items)
}
result := items[i:j] result := items[i:j]
nextLast := "" nextLast := ""

View File

@ -17,6 +17,7 @@ package handler
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -795,13 +796,12 @@ func (a *projectAPI) validateProjectReq(ctx context.Context, req *models.Project
if err != nil { if err != nil {
return fmt.Errorf("failed to get the registry %d: %v", *req.RegistryID, err) return fmt.Errorf("failed to get the registry %d: %v", *req.RegistryID, err)
} }
permitted := false permitted := false
for _, t := range config.GetPermittedRegistryTypesForProxyCache() { if slices.Contains(config.GetPermittedRegistryTypesForProxyCache(), string(registry.Type)) {
if string(registry.Type) == t { permitted = true
permitted = true
break
}
} }
if !permitted { if !permitted {
return errors.BadRequestError(fmt.Errorf("unsupported registry type %s", string(registry.Type))) return errors.BadRequestError(fmt.Errorf("unsupported registry type %s", string(registry.Type)))
} }

View File

@ -48,7 +48,7 @@ func parseScanReportMimeTypes(header *string) []string {
var mimeTypes []string var mimeTypes []string
if header != nil { if header != nil {
for _, mimeType := range strings.Split(*header, ",") { for mimeType := range strings.SplitSeq(*header, ",") {
mimeType = strings.TrimSpace(mimeType) mimeType = strings.TrimSpace(mimeType)
switch mimeType { switch mimeType {
case v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport: case v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport:

View File

@ -3,6 +3,7 @@ package job
import ( import (
"fmt" "fmt"
"math/rand" "math/rand"
"slices"
"github.com/goharbor/harbor/src/common/http" "github.com/goharbor/harbor/src/common/http"
"github.com/goharbor/harbor/src/common/job/models" "github.com/goharbor/harbor/src/common/job/models"
@ -54,10 +55,5 @@ func (mjc *MockJobClient) GetExecutions(uuid string) ([]job.Stats, error) {
} }
func (mjc *MockJobClient) validUUID(uuid string) bool { func (mjc *MockJobClient) validUUID(uuid string) bool {
for _, u := range mjc.JobUUID { return slices.Contains(mjc.JobUUID, uuid)
if uuid == u {
return true
}
}
return false
} }

View File

@ -53,7 +53,7 @@ func OnAnything(obj any, methodName string) *mock.Call {
} }
args := []any{} args := []any{}
for i := 0; i < fnType.NumIn(); i++ { for range fnType.NumIn() {
args = append(args, mock.Anything) args = append(args, mock.Anything)
} }

View File

@ -3,5 +3,6 @@
HARBOR_SRC_FOLDER=$(realpath ../../) HARBOR_SRC_FOLDER=$(realpath ../../)
echo ${HARBOR_SRC_FOLDER} echo ${HARBOR_SRC_FOLDER}
docker run -it --privileged -v /var/log/harbor:/var/log/harbor -v /etc/hosts:/etc/hosts -v ${HARBOR_SRC_FOLDER}:/drone -v ${HARBOR_SRC_FOLDER}/tests/harbor_ca.crt:/ca/ca.crt -v /dev/shm:/dev/shm -w /drone registry.goharbor.io/harbor-ci/goharbor/harbor-e2e-engine:latest-ui /bin/bash # If the testbed network type is private need to set NETWORK_TYPE private, default is public
docker run -it --privileged -v /var/log/harbor:/var/log/harbor -v /etc/hosts:/etc/hosts -v ${HARBOR_SRC_FOLDER}:/drone -v ${HARBOR_SRC_FOLDER}/tests/harbor_ca.crt:/ca/ca.crt -v /dev/shm:/dev/shm -e NETWORK_TYPE=public -w /drone registry.goharbor.io/harbor-ci/goharbor/harbor-e2e-engine:latest-ui /bin/bash

View File

@ -17,6 +17,12 @@ Documentation This resource provides helper functions for docker operations
Library OperatingSystem Library OperatingSystem
Library Process Library Process
*** Variables ***
# Define variables for start dockerd within private network
${DOCKERD_CMD} dockerd
${DOCKERD_ARGS} --iptables=false
${LOG_FILE} ./docker-daemon.log
*** Keywords *** *** Keywords ***
Run Docker Info Run Docker Info
[Arguments] ${docker-params} [Arguments] ${docker-params}
@ -115,12 +121,22 @@ Get Container IP
# docker:1.13-dind # docker:1.13-dind
# If you are running this keyword in a container, make sure it is run with --privileged turned on # If you are running this keyword in a container, make sure it is run with --privileged turned on
Start Docker Daemon Locally Start Docker Daemon Locally
${test_network_type}= Get Environment Variable NETWORK_TYPE public
Log To Console current test_network_type: ${test_network_type}
${pid}= Run pidof dockerd ${pid}= Run pidof dockerd
#${rc} ${output}= Run And Return Rc And Output ./tests/robot-cases/Group0-Util/docker_config.sh #${rc} ${output}= Run And Return Rc And Output ./tests/robot-cases/Group0-Util/docker_config.sh
#Log ${output} #Log ${output}
#Should Be Equal As Integers ${rc} 0 #Should Be Equal As Integers ${rc} 0
Return From Keyword If '${pid}' != '${EMPTY}' Return From Keyword If '${pid}' != '${EMPTY}'
OperatingSystem.File Should Exist /usr/local/bin/dockerd-entrypoint.sh OperatingSystem.File Should Exist /usr/local/bin/dockerd-entrypoint.sh
${handle}= Set Variable ""
IF '${test_network_type}' == 'private'
Log To Console network type is private
${handle}= Start Process ${DOCKERD_CMD} ${DOCKERD_ARGS} stdout=${LOG_FILE} stderr=${LOG_FILE} shell=Tr
ELSE IF '${test_network_type}' == 'public'
Log To Console network type is public
${handle}= Start Process /usr/local/bin/dockerd-entrypoint.sh dockerd>./daemon-docker-local.log 2>&1 shell=True
END
${handle}= Start Process /usr/local/bin/dockerd-entrypoint.sh dockerd>./daemon-local.log 2>&1 shell=True ${handle}= Start Process /usr/local/bin/dockerd-entrypoint.sh dockerd>./daemon-local.log 2>&1 shell=True
Process Should Be Running ${handle} Process Should Be Running ${handle}
FOR ${IDX} IN RANGE 5 FOR ${IDX} IN RANGE 5

View File

@ -4,16 +4,16 @@ In Stratoscale, we really like the idea of API-first services, and we also reall
We saw the go-swagger library, and thought that most of it can really help us. Generating code from We saw the go-swagger library, and thought that most of it can really help us. Generating code from
swagger files is a big problem with a lot of corner cases, and go-swagger is doing great job. swagger files is a big problem with a lot of corner cases, and go-swagger is doing great job.
The one thing that we felt missing, is customization of the server to run with our design principles: The one thing that we felt missing was customization of the server to run with our design principles:
* Custom `main` function * Custom `main` function
* Dependency injection * Dependency injection
* Limited scopes with unit testing. * Limited scope with unit testing.
Also: Also:
* Adding you functions to the generated `configure_swagger_*.go` seems to be a burden. * Adding your functions to the generated `configure_swagger_*.go` seems to be a burden.
* Lack of Interface that the service implement. * Lack of Interface that the service implements.
* Complicated and custom http clients and runtime. * Complicated and custom http clients and runtime.
Those are the changes that this contributor templates are providing: Those are the changes that this contributor templates are providing:
@ -22,7 +22,7 @@ Those are the changes that this contributor templates are providing:
### The new `restapi` package exposes interfaces ### The new `restapi` package exposes interfaces
* Those interfaces can implemented by the developer and are the business logic of the service. * Those interfaces can be implemented by the developer and are the business logic of the service.
* The implementation of those is extensible. * The implementation of those is extensible.
* The implementation is separated from the generated code. * The implementation is separated from the generated code.
@ -32,8 +32,8 @@ The `restapi.Handler` (see [example](./example/restapi/configure_swagger_petstor
a standard `http.Handler` a standard `http.Handler`
* Given objects that implements the business logic, we can create a simple http handler. * Given objects that implements the business logic, we can create a simple http handler.
* This handler is standard go http.Handler, so we can now use any other middleware, library, or framework * This handler is a standard go http.Handler, so we can now use any other middleware, library, or framework
that support it. that supports it.
* This handler is standard, so we understand it better. * This handler is standard, so we understand it better.
## Client ## Client
@ -57,7 +57,7 @@ In the [example package](https://github.com/Stratoscale/swagger/tree/master/exam
### [restapi](https://github.com/Stratoscale/swagger/tree/master/example/restapi) ### [restapi](https://github.com/Stratoscale/swagger/tree/master/example/restapi)
This package is autogenerated and contains the server routing and parameters parsing. This package is autogenerated and contains the server routing and parameter parsing.
The modified version contains `restapi.PetAPI` and `restapi.StoreAPI` which were auto generated. The modified version contains `restapi.PetAPI` and `restapi.StoreAPI` which were auto generated.
@ -116,7 +116,7 @@ Let's look how we use this generated code to build our server.
### [internal](https://github.com/Stratoscale/swagger/tree/master/example/internal) ### [internal](https://github.com/Stratoscale/swagger/tree/master/example/internal)
The `internal` package is **not** auto generated and contains the business logic of our server. The `internal` package is **not** auto generated and contains the business logic of our server.
We can see two structs that implements the `restapi.PetAPI` and `restapi.StoreAPI` interfaces, We can see two structs that implement the `restapi.PetAPI` and `restapi.StoreAPI` interfaces,
needed to make our server work. needed to make our server work.
When adding or removing functions from our REST API, we can just add or remove functions to those When adding or removing functions from our REST API, we can just add or remove functions to those
@ -189,7 +189,7 @@ type SwaggerPetstore {
} }
``` ```
Those fields are objects, which implements interfaces declared in the [pet](./example/client/pet) and Those fields are objects, which implement interfaces declared in the [pet](./example/client/pet) and
[store](./example/client/store) packages: [store](./example/client/store) packages:
For example: For example:
@ -234,7 +234,7 @@ security:
The securityDefinitions section defines different security types that your application can handle. The securityDefinitions section defines different security types that your application can handle.
The supported types by go-swagger are: The supported types by go-swagger are:
* `apiKey` - token that should be able to processed. * `apiKey` - token that should be able to be processed.
* `oauth2` - token and scopes that should be processed. * `oauth2` - token and scopes that should be processed.
* and `basic` - user/password that should be processed. * and `basic` - user/password that should be processed.
@ -253,7 +253,7 @@ paths:
- token: [admin] - token: [admin]
``` ```
Here we overridden the scope of token in the POST /pets URL so that only admin can use this API. Here we have overridden the scope of the token in the POST /pets URL so that only admin can use this API.
Let's see how we can use this functionality. Let's see how we can use this functionality.
@ -289,14 +289,14 @@ type Config struct {
This one is a custom defined function that gets the request and can return an error. This one is a custom defined function that gets the request and can return an error.
If the returned error is not nil, and 403 HTTP error will be returned to the client - here the policy If the returned error is not nil, and 403 HTTP error will be returned to the client - here the policy
enforcement comes to place. enforcement comes into play.
There are two things that this function should be aware of: There are two things that this function should be aware of:
1. The user - it can retrieve the user information from the context: `ctx.Value(restapi.AuthKey).(MyUserType)`. 1. The user - it can retrieve the user information from the context: `ctx.Value(restapi.AuthKey).(MyUserType)`.
Usually, a server will have a function for extracting this user information and returns a concrete Usually, a server will have a function for extracting this user information and returns a concrete
type which could be used by all the routes. type which could be used by all the routes.
2. The route - it can retrieve the route using the go-swagger function: `middleware.MatchedRouteFrom(*http.Request)`. 2. The route - it can retrieve the route using the go-swagger function: `middleware.MatchedRouteFrom(*http.Request)`.
So no need to parse URL and test the request method. So no need to parse the URL and test the request method.
This route struct contains the route information. If for example, we want to check the scopes that were This route struct contains the route information. If for example, we want to check the scopes that were
defined for the current route in the swagger.yaml we can use the code below: defined for the current route in the swagger.yaml we can use the code below: