mirror of https://github.com/goharbor/harbor.git
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
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:
commit
579f14bb29
|
@ -27,7 +27,7 @@ func TestMaxOpenConns(t *testing.T) {
|
|||
|
||||
queryNum := 200
|
||||
results := make([]bool, queryNum)
|
||||
for i := 0; i < queryNum; i++ {
|
||||
for i := range queryNum {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
|
|
@ -142,7 +142,7 @@ func ArrayEqual(arrayA, arrayB []int) bool {
|
|||
return false
|
||||
}
|
||||
size := len(arrayA)
|
||||
for i := 0; i < size; i++ {
|
||||
for i := range size {
|
||||
if arrayA[i] != arrayB[i] {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ func BenchmarkProjectEvaluator(b *testing.B) {
|
|||
resource := NewNamespace(public.ProjectID).Resource(rbac.ResourceRepository)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
evaluator.HasPermission(context.TODO(), resource, rbac.ActionPull)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ func GenerateRandomStringWithLen(length int) string {
|
|||
if err != nil {
|
||||
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]
|
||||
}
|
||||
return string(result)
|
||||
|
@ -337,13 +337,3 @@ func MostMatchSorter(a, b string, matchWord string) bool {
|
|||
func IsLocalPath(path string) bool {
|
||||
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
|
||||
}
|
||||
|
|
|
@ -66,8 +66,7 @@ func parseV1alpha1SkipList(artifact *artifact.Artifact, manifest *v1.Manifest) {
|
|||
skipListAnnotationKey := fmt.Sprintf("%s.%s.%s", AnnotationPrefix, V1alpha1, SkipList)
|
||||
skipList, ok := manifest.Config.Annotations[skipListAnnotationKey]
|
||||
if ok {
|
||||
skipKeyList := strings.Split(skipList, ",")
|
||||
for _, skipKey := range skipKeyList {
|
||||
for skipKey := range strings.SplitSeq(skipList, ",") {
|
||||
delete(metadata, skipKey)
|
||||
}
|
||||
artifact.ExtraAttrs = metadata
|
||||
|
|
|
@ -156,7 +156,7 @@ func TestAddNode(t *testing.T) {
|
|||
// Verify the path exists.
|
||||
current := root
|
||||
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 == "" {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ func (suite *ControllerTestSuite) TestGet() {
|
|||
|
||||
func (suite *ControllerTestSuite) TestSync() {
|
||||
var references []distribution.Descriptor
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
references = append(references, distribution.Descriptor{
|
||||
MediaType: fmt.Sprintf("media type %d", i),
|
||||
Digest: suite.Digest(),
|
||||
|
|
|
@ -206,7 +206,7 @@ func maxValueLimitedByLength(length int) int64 {
|
|||
var value int64
|
||||
// the times for multiple, should *10 for every time
|
||||
times := 1
|
||||
for i := 0; i < length; i++ {
|
||||
for range length {
|
||||
value = value + int64(9*times)
|
||||
times = times * 10
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ func constructScanImagePayload(ctx context.Context, event *event.ScanImageEvent,
|
|||
// Wait for reasonable time to make sure the report is ready
|
||||
// Interval=500ms and total time = 5s
|
||||
// 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
|
||||
if re, err := scan.DefaultController.GetReport(ctx, art, []string{v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport}); err == nil {
|
||||
if len(re) > 0 && len(re[0].Report) > 0 {
|
||||
|
|
|
@ -48,7 +48,7 @@ func (c *controller) GetHealth(_ context.Context) *OverallHealthStatus {
|
|||
for name, checker := range registry {
|
||||
go check(name, checker, timeout, ch)
|
||||
}
|
||||
for i := 0; i < len(registry); i++ {
|
||||
for range len(registry) {
|
||||
componentStatus := <-ch
|
||||
if len(componentStatus.Error) != 0 {
|
||||
isHealthy = false
|
||||
|
|
|
@ -17,6 +17,7 @@ package jobmonitor
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -278,12 +279,7 @@ func (w *monitorController) ListQueues(ctx context.Context) ([]*jm.Queue, error)
|
|||
}
|
||||
|
||||
func skippedUnusedJobType(jobType string) bool {
|
||||
for _, t := range skippedJobTypes {
|
||||
if jobType == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(skippedJobTypes, jobType)
|
||||
}
|
||||
|
||||
func (w *monitorController) PauseJobQueues(ctx context.Context, jobType string) error {
|
||||
|
|
|
@ -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
|
||||
var newMan distribution.Manifest
|
||||
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)
|
||||
time.Sleep(sleepIntervalSec * time.Second)
|
||||
newMan, err = m.updateManifestList(ctx, repo, man)
|
||||
|
@ -177,7 +177,7 @@ type ManifestCache struct {
|
|||
// CacheContent ...
|
||||
func (m *ManifestCache) CacheContent(ctx context.Context, remoteRepo string, man distribution.Manifest, art lib.ArtifactInfo, r RemoteInterface, _ string) {
|
||||
var waitBlobs []distribution.Descriptor
|
||||
for n := 0; n < maxManifestWait; n++ {
|
||||
for n := range maxManifestWait {
|
||||
time.Sleep(sleepIntervalSec * time.Second)
|
||||
waitBlobs = m.local.CheckDependencies(ctx, art.Repository, man)
|
||||
if len(waitBlobs) == 0 {
|
||||
|
|
|
@ -78,13 +78,13 @@ func (suite *RefreshForProjectsTestSuite) TestRefreshForProjects() {
|
|||
|
||||
startProjectID := rand.Int63()
|
||||
var firstPageProjects, secondPageProjects []*models.Project
|
||||
for i := 0; i < 50; i++ {
|
||||
for i := range 50 {
|
||||
firstPageProjects = append(firstPageProjects, &models.Project{
|
||||
ProjectID: startProjectID + int64(i),
|
||||
})
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := range 10 {
|
||||
secondPageProjects = append(secondPageProjects, &models.Project{
|
||||
ProjectID: startProjectID + 50 + int64(i),
|
||||
})
|
||||
|
|
|
@ -423,10 +423,7 @@ func (t *transfer) copyBlobByChunk(srcRepo, dstRepo, digest string, sizeFromDesc
|
|||
// update the start and end for upload
|
||||
*start = *end + 1
|
||||
// since both ends are closed intervals, it is necessary to subtract one byte
|
||||
*end = *start + replicationChunkSize - 1
|
||||
if *end >= endRange {
|
||||
*end = endRange
|
||||
}
|
||||
*end = min(*start+replicationChunkSize-1, endRange)
|
||||
|
||||
t.logger.Infof("copying the blob chunk: %d-%d/%d", *start, *end, sizeFromDescriptor)
|
||||
_, data, err := t.src.PullBlobChunk(srcRepo, digest, sizeFromDescriptor, *start, *end)
|
||||
|
|
|
@ -16,6 +16,7 @@ package scan
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/goharbor/harbor/src/controller/artifact"
|
||||
"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
|
||||
// https://github.com/goharbor/pluggable-scanner-spec/issues/2
|
||||
allowlist := []string{image.ArtifactTypeImage}
|
||||
for _, t := range allowlist {
|
||||
if a.Type == t {
|
||||
return r.HasCapability(a.ManifestMediaType)
|
||||
}
|
||||
if slices.Contains(allowlist, a.Type) {
|
||||
return r.HasCapability(a.ManifestMediaType)
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
|
@ -17,6 +17,7 @@ package scanner
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -383,13 +384,7 @@ var (
|
|||
)
|
||||
|
||||
func isReservedName(name string) bool {
|
||||
for _, reservedName := range reservedNames {
|
||||
if name == reservedName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(reservedNames, name)
|
||||
}
|
||||
|
||||
// MetadataResult metadata or error saved in cache
|
||||
|
|
|
@ -150,7 +150,7 @@ func (l *Auth) attachGroupParallel(ctx context.Context, ldapUsers []model.User,
|
|||
g := new(errgroup.Group)
|
||||
g.SetLimit(workerCount)
|
||||
|
||||
for i := 0; i < workerCount; i++ {
|
||||
for i := range workerCount {
|
||||
curIndex := i
|
||||
g.Go(func() error {
|
||||
userGroups := make([]ugModel.UserGroup, 0)
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"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 {
|
||||
if actions, ok := f.rcActions[resource]; ok {
|
||||
for _, a := range actions {
|
||||
if a == action {
|
||||
return true
|
||||
}
|
||||
if slices.Contains(actions, action) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -16,6 +16,7 @@ package session
|
|||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"maps"
|
||||
|
||||
"github.com/beego/beego/v2/server/web/session"
|
||||
|
||||
|
@ -51,14 +52,10 @@ func (*gobCodec) Decode(data []byte, v any) error {
|
|||
|
||||
switch in := v.(type) {
|
||||
case map[any]any:
|
||||
for k, v := range vm {
|
||||
in[k] = v
|
||||
}
|
||||
maps.Copy(in, vm)
|
||||
case *map[any]any:
|
||||
m := *in
|
||||
for k, v := range vm {
|
||||
m[k] = v
|
||||
}
|
||||
maps.Copy(m, vm)
|
||||
default:
|
||||
return errors.Errorf("object type invalid, %#v", v)
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -116,9 +117,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
|
|||
|
||||
// Copy properties
|
||||
if len(c.properties) > 0 {
|
||||
for k, v := range c.properties {
|
||||
jContext.properties[k] = v
|
||||
}
|
||||
maps.Copy(jContext.properties, c.properties)
|
||||
}
|
||||
|
||||
// Refresh config properties
|
||||
|
@ -128,9 +127,7 @@ func (c *Context) Build(tracker job.Tracker) (job.Context, error) {
|
|||
}
|
||||
|
||||
props := c.cfgMgr.GetAll(c.sysContext)
|
||||
for k, v := range props {
|
||||
jContext.properties[k] = v
|
||||
}
|
||||
maps.Copy(jContext.properties, props)
|
||||
|
||||
// Set loggers for job
|
||||
c.lock.Lock()
|
||||
|
@ -199,10 +196,7 @@ func createLoggers(jobID string) (logger.Interface, error) {
|
|||
if lc.Name == logger.NameFile || lc.Name == logger.NameDB {
|
||||
// Need extra param
|
||||
fSettings := map[string]any{}
|
||||
for k, v := range lc.Settings {
|
||||
// Copy settings
|
||||
fSettings[k] = v
|
||||
}
|
||||
maps.Copy(fSettings, lc.Settings)
|
||||
if lc.Name == logger.NameFile {
|
||||
// Append file name param
|
||||
fSettings["filename"] = fmt.Sprintf("%s.log", jobID)
|
||||
|
|
|
@ -17,6 +17,7 @@ package impl
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"maps"
|
||||
|
||||
o "github.com/beego/beego/v2/client/orm"
|
||||
|
||||
|
@ -61,9 +62,7 @@ func (dc *DefaultContext) Build(t job.Tracker) (job.Context, error) {
|
|||
|
||||
// Copy properties
|
||||
if len(dc.properties) > 0 {
|
||||
for k, v := range dc.properties {
|
||||
jContext.properties[k] = v
|
||||
}
|
||||
maps.Copy(jContext.properties, dc.properties)
|
||||
}
|
||||
|
||||
// Set loggers for job
|
||||
|
|
|
@ -299,10 +299,7 @@ func (gc *GarbageCollector) sweep(ctx job.Context) error {
|
|||
blobChunkCount := (total + blobChunkSize - 1) / blobChunkSize
|
||||
blobChunks := make([][]*blobModels.Blob, blobChunkCount)
|
||||
for i, start := 0, 0; i < blobChunkCount; i, start = i+1, start+blobChunkSize {
|
||||
end := start + blobChunkSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
end := min(start+blobChunkSize, total)
|
||||
blobChunks[i] = gc.deleteSet[start:end]
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ func TestDelKeys(t *testing.T) {
|
|||
// helper function
|
||||
// mock the data in the redis
|
||||
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)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package scandataexport
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
@ -173,9 +174,8 @@ func (sde *ScanDataExport) updateExecAttributes(ctx job.Context, params job.Para
|
|||
}
|
||||
// copy old extra
|
||||
attrsToUpdate := exec.ExtraAttrs
|
||||
for k, v := range attrs {
|
||||
attrsToUpdate[k] = v
|
||||
}
|
||||
maps.Copy(attrsToUpdate, attrs)
|
||||
|
||||
return sde.execMgr.UpdateExtraAttrs(ctx.SystemContext(), execID, attrsToUpdate)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -97,8 +96,7 @@ func TestGetLoggersMulti(t *testing.T) {
|
|||
|
||||
// Test getting sweepers
|
||||
func TestGetSweeper(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
_, err := GetSweeper(ctx)
|
||||
if err == nil {
|
||||
|
@ -170,8 +168,7 @@ func TestGetGetter(t *testing.T) {
|
|||
|
||||
// Test init
|
||||
func TestLoggerInit(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := t.Context()
|
||||
|
||||
oldJobLoggerCfg := config.DefaultConfig.JobLoggerConfigs
|
||||
oldLoggerCfg := config.DefaultConfig.LoggerConfigs
|
||||
|
|
|
@ -99,10 +99,8 @@ func tailLogFile(filename string, limit int64) ([]byte, error) {
|
|||
}
|
||||
defer fi.Close()
|
||||
|
||||
pos := size - sizeToRead
|
||||
if pos < 0 {
|
||||
pos = 0
|
||||
}
|
||||
pos := max(size-sizeToRead, 0)
|
||||
|
||||
if pos != 0 {
|
||||
_, err = fi.Seek(pos, 0)
|
||||
if err != nil {
|
||||
|
|
|
@ -16,6 +16,7 @@ package logger
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/goharbor/harbor/src/jobservice/logger/backend"
|
||||
|
@ -89,13 +90,7 @@ func IsKnownLevel(level string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
for _, lvl := range debugLevels {
|
||||
if lvl == strings.ToUpper(level) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.Contains(debugLevels, strings.ToUpper(level))
|
||||
}
|
||||
|
||||
// GetLoggerName return a logger name by Interface
|
||||
|
|
|
@ -17,6 +17,7 @@ package period
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
|
@ -303,9 +304,7 @@ func cloneParameters(params job.Parameters, epoch int64) job.Parameters {
|
|||
p := make(job.Parameters)
|
||||
|
||||
// Clone parameters to a new param map
|
||||
for k, v := range params {
|
||||
p[k] = v
|
||||
}
|
||||
maps.Copy(p, params)
|
||||
|
||||
p[PeriodicExecutionMark] = fmt.Sprintf("%d", epoch)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
func BenchmarkDefaultCodecEncode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
codec.Encode("abcdefghigklmopqrztuvwxyz")
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func BenchmarkDefaultCodecEncode(b *testing.B) {
|
|||
func BenchmarkDefaultCodecDecode(b *testing.B) {
|
||||
data := []byte("abcdefghigklmopqrztuvwxyz")
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
var str string
|
||||
codec.Decode(data, &str)
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (suite *FetchOrSaveTestSuite) TestSaveCalledOnlyOneTime() {
|
|||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
for range 1000 {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
|
|
|
@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
|
|||
|
||||
func (suite *CacheTestSuite) TestScan() {
|
||||
seed := func(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
key := fmt.Sprintf("test-scan-%d", i)
|
||||
err := suite.cache.Save(suite.ctx, key, "")
|
||||
suite.NoError(err)
|
||||
}
|
||||
}
|
||||
clean := func(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
key := fmt.Sprintf("test-scan-%d", i)
|
||||
err := suite.cache.Delete(suite.ctx, key)
|
||||
suite.NoError(err)
|
||||
|
|
|
@ -112,14 +112,14 @@ func (suite *CacheTestSuite) TestPing() {
|
|||
|
||||
func (suite *CacheTestSuite) TestScan() {
|
||||
seed := func(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
key := fmt.Sprintf("test-scan-%d", i)
|
||||
err := suite.cache.Save(suite.ctx, key, "")
|
||||
suite.NoError(err)
|
||||
}
|
||||
}
|
||||
clean := func(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
key := fmt.Sprintf("test-scan-%d", i)
|
||||
err := suite.cache.Delete(suite.ctx, key)
|
||||
suite.NoError(err)
|
||||
|
|
|
@ -26,7 +26,7 @@ func TestKeyMutex(t *testing.T) {
|
|||
key := "key"
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
|
|
|
@ -220,7 +220,7 @@ func RobotPrefix(ctx context.Context) string {
|
|||
// SplitAndTrim ...
|
||||
func SplitAndTrim(s, sep string) []string {
|
||||
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 {
|
||||
res = append(res, e)
|
||||
}
|
||||
|
@ -269,8 +269,7 @@ func AuditLogEventEnabled(ctx context.Context, eventType string) bool {
|
|||
return true
|
||||
}
|
||||
disableListStr := DefaultMgr().Get(ctx, common.AuditLogEventsDisabled).GetString()
|
||||
disableList := strings.Split(disableListStr, ",")
|
||||
for _, t := range disableList {
|
||||
for t := range strings.SplitSeq(disableListStr, ",") {
|
||||
tName := strings.TrimSpace(t)
|
||||
if strings.EqualFold(tName, eventType) {
|
||||
return false
|
||||
|
|
|
@ -27,7 +27,7 @@ func TestAddTask(t *testing.T) {
|
|||
|
||||
taskNum := 3
|
||||
taskInterval := time.Duration(0)
|
||||
for i := 0; i < taskNum; i++ {
|
||||
for i := range taskNum {
|
||||
fn := func(ctx context.Context) {
|
||||
t.Logf("Task %d is running...", i)
|
||||
}
|
||||
|
@ -64,8 +64,7 @@ func TestStartAndStop(t *testing.T) {
|
|||
|
||||
pool.tasks = []*task{t1, t2}
|
||||
|
||||
ctx1, cancel1 := context.WithCancel(context.Background())
|
||||
defer cancel1()
|
||||
ctx1 := t.Context()
|
||||
pool.Start(ctx1)
|
||||
|
||||
// Let it run for a bit
|
||||
|
|
|
@ -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"
|
||||
func ParseLinks(str string) Links {
|
||||
var links Links
|
||||
for _, lk := range strings.Split(str, ",") {
|
||||
for lk := range strings.SplitSeq(str, ",") {
|
||||
link := &Link{
|
||||
Attrs: map[string]string{},
|
||||
}
|
||||
for _, attr := range strings.Split(lk, ";") {
|
||||
for attr := range strings.SplitSeq(lk, ";") {
|
||||
attr = strings.TrimSpace(attr)
|
||||
if len(attr) == 0 {
|
||||
continue
|
||||
|
|
|
@ -17,6 +17,7 @@ package log
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
|
@ -122,12 +123,8 @@ func (l *Logger) WithFields(fields Fields) *Logger {
|
|||
|
||||
if len(fields) > 0 {
|
||||
copyFields := make(map[string]any, len(l.fields)+len(fields))
|
||||
for key, value := range l.fields {
|
||||
copyFields[key] = value
|
||||
}
|
||||
for key, value := range fields {
|
||||
copyFields[key] = value
|
||||
}
|
||||
maps.Copy(copyFields, l.fields)
|
||||
maps.Copy(copyFields, fields)
|
||||
|
||||
sortedKeys := make([]string, 0, len(copyFields))
|
||||
for key := range copyFields {
|
||||
|
|
|
@ -79,7 +79,7 @@ func parseModel(model any) *metadata {
|
|||
Keys: map[string]*key{},
|
||||
}
|
||||
// parse fields of the provided model
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
for i := range t.NumField() {
|
||||
field := t.Field(i)
|
||||
orm := field.Tag.Get("orm")
|
||||
// isn't the database column, skip
|
||||
|
@ -107,7 +107,7 @@ func parseModel(model any) *metadata {
|
|||
}
|
||||
|
||||
// parse filter methods of the provided model
|
||||
for i := 0; i < ptr.NumMethod(); i++ {
|
||||
for i := range ptr.NumMethod() {
|
||||
methodName := ptr.Method(i).Name
|
||||
if !strings.HasPrefix(methodName, "FilterBy") {
|
||||
continue
|
||||
|
@ -173,7 +173,7 @@ func parseFilterable(field reflect.StructField) bool {
|
|||
// }
|
||||
func parseSortable(field reflect.StructField) (*q.Sort, bool) {
|
||||
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
|
||||
if item == "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"
|
||||
func parseColumn(field reflect.StructField) string {
|
||||
column := ""
|
||||
for _, item := range strings.Split(field.Tag.Get("orm"), ";") {
|
||||
for item := range strings.SplitSeq(field.Tag.Get("orm"), ";") {
|
||||
if !strings.HasPrefix(item, "column") {
|
||||
continue
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ func snakeCase(str string) string {
|
|||
runes := []rune(str)
|
||||
|
||||
var out []rune
|
||||
for i := 0; i < len(runes); i++ {
|
||||
for i := range len(runes) {
|
||||
if i > 0 &&
|
||||
(unicode.IsUpper(runes[i])) &&
|
||||
((i+1 < len(runes) && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
|
||||
|
|
|
@ -271,7 +271,7 @@ func Escape(str string) string {
|
|||
// e.g. n=3, returns "?,?,?"
|
||||
func ParamPlaceholderForIn(n int) string {
|
||||
placeholders := []string{}
|
||||
for i := 0; i < n; i++ {
|
||||
for range n {
|
||||
placeholders = append(placeholders, "?")
|
||||
}
|
||||
return strings.Join(placeholders, ",")
|
||||
|
|
|
@ -387,7 +387,7 @@ func (suite *OrmSuite) TestReadOrCreateParallel() {
|
|||
arr := make([]int, count)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < count; i++ {
|
||||
for i := range count {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
|
|
@ -58,8 +58,8 @@ func parseKeywords(q string) (map[string]any, error) {
|
|||
} else {
|
||||
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)
|
||||
if len(strs) != 2 || len(strs[0]) == 0 || len(strs[1]) == 0 {
|
||||
return nil, errors.New(nil).
|
||||
|
@ -82,7 +82,7 @@ func ParseSorting(sort string) []*Sort {
|
|||
return []*Sort{}
|
||||
}
|
||||
var sorts []*Sort
|
||||
for _, sorting := range strings.Split(sort, ",") {
|
||||
for sorting := range strings.SplitSeq(sort, ",") {
|
||||
key := sorting
|
||||
desc := false
|
||||
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 ")"`)
|
||||
}
|
||||
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)
|
||||
if s, ok := v.(string); ok && len(s) == 0 {
|
||||
continue
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
package q
|
||||
|
||||
import "maps"
|
||||
|
||||
// KeyWords ...
|
||||
type KeyWords = map[string]any
|
||||
|
||||
|
@ -56,9 +58,8 @@ func MustClone(query *Query) *Query {
|
|||
if query != nil {
|
||||
q.PageNumber = query.PageNumber
|
||||
q.PageSize = query.PageSize
|
||||
for k, v := range query.Keywords {
|
||||
q.Keywords[k] = v
|
||||
}
|
||||
maps.Copy(q.Keywords, query.Keywords)
|
||||
|
||||
for _, sort := range query.Sorts {
|
||||
q.Sorts = append(q.Sorts, &Sort{
|
||||
Key: sort.Key,
|
||||
|
|
|
@ -34,7 +34,7 @@ func TestGetRegistryClient(t *testing.T) {
|
|||
assert.NotNil(t, client)
|
||||
|
||||
// multiple calls should return the same client
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
newClient, err := GetRegistryClient()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, client, newClient)
|
||||
|
@ -55,7 +55,7 @@ func TestGetHarborClient(t *testing.T) {
|
|||
assert.NotNil(t, client)
|
||||
|
||||
// multiple calls should return the same client
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
newClient, err := GetHarborClient()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, client, newClient)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
package index
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
|
@ -80,11 +81,9 @@ func Get(kind, decoration, pattern, extras string) (selector.Selector, error) {
|
|||
}
|
||||
|
||||
item := v.(*indexedItem)
|
||||
for _, dec := range item.Meta.Decorations {
|
||||
if dec == decoration {
|
||||
factory := item.Factory
|
||||
return factory(decoration, pattern, extras), nil
|
||||
}
|
||||
if slices.Contains(item.Meta.Decorations, decoration) {
|
||||
factory := item.Factory
|
||||
return factory(decoration, pattern, extras), nil
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("decoration %s of selector %s is not supported", decoration, kind)
|
||||
|
|
|
@ -28,7 +28,7 @@ func (s *testSuite) TestSetAndGet() {
|
|||
s.Nil(err)
|
||||
s.Nil(l)
|
||||
var longList []models.CVEAllowlistItem
|
||||
for i := 0; i < 50; i++ {
|
||||
for range 50 {
|
||||
longList = append(longList, models.CVEAllowlistItem{CVEID: "CVE-1999-0067"})
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ package allowlist
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
models2 "github.com/goharbor/harbor/src/pkg/allowlist/models"
|
||||
)
|
||||
|
@ -45,6 +46,13 @@ func IsInvalidErr(err error) bool {
|
|||
func Validate(wl models2.CVEAllowlist) error {
|
||||
m := map[string]struct{}{}
|
||||
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 {
|
||||
return &invalidErr{fmt.Sprintf("duplicate CVE ID in allowlist: %s", it.CVEID)}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,22 @@ func TestValidate(t *testing.T) {
|
|||
l models2.CVEAllowlist
|
||||
noError bool
|
||||
}{
|
||||
{
|
||||
l: models2.CVEAllowlist{
|
||||
Items: []models2.CVEAllowlistItem{
|
||||
{CVEID: ""},
|
||||
},
|
||||
},
|
||||
noError: false,
|
||||
},
|
||||
{
|
||||
l: models2.CVEAllowlist{
|
||||
Items: []models2.CVEAllowlistItem{
|
||||
{CVEID: " "},
|
||||
},
|
||||
},
|
||||
noError: false,
|
||||
},
|
||||
{
|
||||
l: models2.CVEAllowlist{
|
||||
Items: nil,
|
||||
|
|
|
@ -16,11 +16,11 @@ package dao
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
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/log"
|
||||
"github.com/goharbor/harbor/src/lib/orm"
|
||||
|
@ -197,7 +197,7 @@ func permitEventTypes(includeEventTypes []string) []string {
|
|||
var filterEvents []string
|
||||
for _, e := range includeEventTypes {
|
||||
event := strings.ToLower(e)
|
||||
if utils.StringInSlice(event, model.EventTypes) {
|
||||
if slices.Contains(model.EventTypes, event) {
|
||||
filterEvents = append(filterEvents, e)
|
||||
} else if event == model.OtherEvents { // include all other events
|
||||
filterEvents = append(filterEvents, model.OtherEventTypes...)
|
||||
|
|
|
@ -75,7 +75,7 @@ func (ok *ObjectKey) Format(keysAndValues ...any) (string, error) {
|
|||
}
|
||||
|
||||
s := ok.namespace
|
||||
for i := 0; i < len(keysAndValues); i++ {
|
||||
for i := range len(keysAndValues) {
|
||||
// even is key
|
||||
if i%2 == 0 {
|
||||
key, match := keysAndValues[i].(string)
|
||||
|
|
|
@ -16,6 +16,7 @@ package inmemory
|
|||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"sync"
|
||||
|
||||
"github.com/goharbor/harbor/src/common"
|
||||
|
@ -41,9 +42,7 @@ func (d *Driver) Load(context.Context) (map[string]any, error) {
|
|||
d.Lock()
|
||||
defer d.Unlock()
|
||||
res := make(map[string]any)
|
||||
for k, v := range d.cfgMap {
|
||||
res[k] = v
|
||||
}
|
||||
maps.Copy(res, d.cfgMap)
|
||||
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 {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
for k, v := range cfg {
|
||||
d.cfgMap[k] = v
|
||||
}
|
||||
maps.Copy(d.cfgMap, cfg)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -66,9 +67,7 @@ func ConfigPutHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for k, v := range cfgs {
|
||||
configMapForTest[k] = v
|
||||
}
|
||||
maps.Copy(configMapForTest, cfgs)
|
||||
}
|
||||
|
||||
func TestHTTPDriver_Save(t *testing.T) {
|
||||
|
|
|
@ -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
|
||||
for startIndex := int64(0); startIndex < int64(size); startIndex += batchSize {
|
||||
endIndex := startIndex + batchSize
|
||||
if endIndex > int64(size) {
|
||||
endIndex = int64(size)
|
||||
}
|
||||
endIndex := min(startIndex+batchSize, int64(size))
|
||||
jobs, err := redis.Strings(conn.Do("LRANGE", redisKeyJobQueue, startIndex, endIndex))
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
|
|
|
@ -59,7 +59,7 @@ func (s *RedisClientTestSuite) TestUntrackJobStatusInBatch() {
|
|||
jobIDs := make([]string, 0)
|
||||
conn := s.redisClient.redisPool.Get()
|
||||
defer conn.Close()
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
k := utils.GenerateRandomStringWithLen(10)
|
||||
jobIDs = append(jobIDs, k)
|
||||
key := rds.KeyJobStats(fmt.Sprintf("{%s}", s.redisClient.namespace), k)
|
||||
|
@ -92,7 +92,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
|
|||
}
|
||||
conn := s.redisClient.redisPool.Get()
|
||||
defer conn.Close()
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
job := jobInfo{
|
||||
ID: utils.GenerateRandomStringWithLen(10),
|
||||
Params: utils.GenerateRandomStringWithLen(10),
|
||||
|
@ -107,7 +107,7 @@ func (s *RedisClientTestSuite) TestStopPendingJobs() {
|
|||
}
|
||||
}
|
||||
// job without id
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
job := jobInfo{
|
||||
Params: utils.GenerateRandomStringWithLen(10),
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ func TestConcurrentPublish(t *testing.T) {
|
|||
}
|
||||
|
||||
// Publish in a short interval.
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
Publish(context.TODO(), "topic1", 100)
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -392,11 +393,8 @@ func userInfoFromClaims(c claimsProvider, setting cfgModels.OIDCSetting) (*UserI
|
|||
}
|
||||
res.Groups, res.hasGroupClaim = groupsFromClaims(c, setting.GroupsClaim)
|
||||
if len(setting.AdminGroup) > 0 {
|
||||
for _, g := range res.Groups {
|
||||
if g == setting.AdminGroup {
|
||||
res.AdminGroupMember = true
|
||||
break
|
||||
}
|
||||
if slices.Contains(res.Groups, setting.AdminGroup) {
|
||||
res.AdminGroupMember = true
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
|
|
@ -36,13 +36,13 @@ func TestExpiration(t *testing.T) {
|
|||
|
||||
func TestGC(t *testing.T) {
|
||||
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)
|
||||
manager.Generate(rn)
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
assert.Equal(t, uint64(10), manager.size)
|
||||
for i := 0; i < 1000; i++ {
|
||||
for i := range 1000 {
|
||||
rn := fmt.Sprintf("project%d/redis", i)
|
||||
manager.Generate(rn)
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ func compileRegexpEveryTime(url string) (string, string, error) {
|
|||
}
|
||||
|
||||
func BenchmarkGetAccountRegion(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
for _, url := range urlForBenchmark {
|
||||
parseAccountRegion(url)
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ func BenchmarkGetAccountRegion(b *testing.B) {
|
|||
}
|
||||
|
||||
func BenchmarkCompileRegexpEveryTime(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
for _, url := range urlForBenchmark {
|
||||
compileRegexpEveryTime(url)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
|
||||
|
@ -195,10 +196,8 @@ func (a adapter) listGcrTagsByRef(repository, reference string) ([]string, strin
|
|||
}
|
||||
// for tag as reference
|
||||
for d, m := range tgs.Manifest {
|
||||
for _, t := range m.Tag {
|
||||
if t == reference {
|
||||
return m.Tag, d, nil
|
||||
}
|
||||
if slices.Contains(m.Tag, reference) {
|
||||
return m.Tag, d, nil
|
||||
}
|
||||
}
|
||||
return nil, "", nil
|
||||
|
|
|
@ -40,7 +40,7 @@ func IsSpecificPath(path string) ([]string, bool) {
|
|||
return nil, false
|
||||
}
|
||||
components := [][]string{}
|
||||
for _, component := range strings.Split(path, "/") {
|
||||
for component := range strings.SplitSeq(path, "/") {
|
||||
strs, ok := IsSpecificPathComponent(component)
|
||||
if !ok {
|
||||
return nil, false
|
||||
|
@ -113,8 +113,7 @@ func IsSpecificPathComponent(component string) ([]string, bool) {
|
|||
suffix = component[j+1:]
|
||||
}
|
||||
components := []string{}
|
||||
strs := strings.Split(component[i+1:j], ",")
|
||||
for _, str := range strs {
|
||||
for str := range strings.SplitSeq(component[i+1:j], ",") {
|
||||
components = append(components, prefix+str+suffix)
|
||||
}
|
||||
return components, true
|
||||
|
|
|
@ -49,11 +49,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
|
|||
return activeTime(artifacts[i]) > activeTime(artifacts[j])
|
||||
})
|
||||
|
||||
i := e.k
|
||||
if i > len(artifacts) {
|
||||
i = len(artifacts)
|
||||
}
|
||||
|
||||
i := min(e.k, len(artifacts))
|
||||
return artifacts[:i], nil
|
||||
}
|
||||
|
||||
|
|
|
@ -46,11 +46,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
|
|||
return artifacts[i].PulledTime > artifacts[j].PulledTime
|
||||
})
|
||||
|
||||
i := e.n
|
||||
if i > len(artifacts) {
|
||||
i = len(artifacts)
|
||||
}
|
||||
|
||||
i := min(e.n, len(artifacts))
|
||||
return artifacts[:i], nil
|
||||
}
|
||||
|
||||
|
|
|
@ -48,11 +48,7 @@ func (e *evaluator) Process(artifacts []*selector.Candidate) ([]*selector.Candid
|
|||
return artifacts[i].PushedTime > artifacts[j].PushedTime
|
||||
})
|
||||
|
||||
i := e.k
|
||||
if i > len(artifacts) {
|
||||
i = len(artifacts)
|
||||
}
|
||||
|
||||
i := min(e.k, len(artifacts))
|
||||
return artifacts[:i], nil
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ package scanner
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib"
|
||||
|
@ -140,15 +141,9 @@ func (r *Registration) HasCapability(manifestMimeType string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
for _, capability := range r.Metadata.Capabilities {
|
||||
for _, mt := range capability.ConsumesMimeTypes {
|
||||
if mt == manifestMimeType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.ContainsFunc(r.Metadata.Capabilities, func(c *v1.ScannerCapability) bool {
|
||||
return slices.Contains(c.ConsumesMimeTypes, manifestMimeType)
|
||||
})
|
||||
}
|
||||
|
||||
// GetProducesMimeTypes returns produces mime types for the artifact
|
||||
|
@ -162,10 +157,8 @@ func (r *Registration) GetProducesMimeTypes(mimeType string, scanType string) []
|
|||
capType = v1.ScanTypeVulnerability
|
||||
}
|
||||
if scanType == capType {
|
||||
for _, mt := range capability.ConsumesMimeTypes {
|
||||
if mt == mimeType {
|
||||
return capability.ProducesMimeTypes
|
||||
}
|
||||
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
|
||||
return capability.ProducesMimeTypes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,10 +173,8 @@ func (r *Registration) GetCapability(mimeType string) *v1.ScannerCapability {
|
|||
}
|
||||
|
||||
for _, capability := range r.Metadata.Capabilities {
|
||||
for _, mt := range capability.ConsumesMimeTypes {
|
||||
if mt == mimeType {
|
||||
return capability
|
||||
}
|
||||
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
|
||||
return capability
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ package export
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/utils"
|
||||
"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
|
||||
// for^2, we can convert to use map to reduce the time complex if needed. )
|
||||
for _, label := range art.Labels {
|
||||
for _, labelID := range labelIDs {
|
||||
if labelID == label.ID {
|
||||
return true
|
||||
}
|
||||
if slices.Contains(labelIDs, label.ID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -101,10 +101,7 @@ func GenerateNativeSummary(r *scan.Report, _ ...Option) (any, error) {
|
|||
sum.ReportID = r.UUID
|
||||
sum.StartTime = r.StartTime
|
||||
sum.EndTime = r.EndTime
|
||||
sum.Duration = r.EndTime.Unix() - r.StartTime.Unix()
|
||||
if sum.Duration < 0 {
|
||||
sum.Duration = 0
|
||||
}
|
||||
sum.Duration = max(r.EndTime.Unix()-r.StartTime.Unix(), 0)
|
||||
|
||||
sum.ScanStatus = job.ErrorStatus.String()
|
||||
if job.Status(r.Status).Code() != -1 {
|
||||
|
|
|
@ -17,6 +17,7 @@ package v1
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/goharbor/harbor/src/lib/errors"
|
||||
)
|
||||
|
@ -95,26 +96,15 @@ func (md *ScannerAdapterMetadata) Validate() error {
|
|||
|
||||
for _, ca := range md.Capabilities {
|
||||
// v1.MimeTypeDockerArtifact is required now
|
||||
found := false
|
||||
for _, cm := range ca.ConsumesMimeTypes {
|
||||
if cm == MimeTypeDockerArtifact {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
found := slices.Contains(ca.ConsumesMimeTypes, MimeTypeDockerArtifact)
|
||||
if !found {
|
||||
return errors.Errorf("missing %s in consumes_mime_types", MimeTypeDockerArtifact)
|
||||
}
|
||||
|
||||
// either of v1.MimeTypeNativeReport OR v1.MimeTypeGenericVulnerabilityReport is required
|
||||
found = false
|
||||
for _, pm := range ca.ProducesMimeTypes {
|
||||
if isSupportedMimeType(pm) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
found = slices.ContainsFunc(ca.ProducesMimeTypes, func(pm string) bool {
|
||||
return isSupportedMimeType(pm)
|
||||
})
|
||||
if !found {
|
||||
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 {
|
||||
for _, mt := range supportedMimeTypes {
|
||||
if mt == mimeType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(supportedMimeTypes, mimeType)
|
||||
}
|
||||
|
||||
// HasCapability returns true when mine type of the artifact support by the scanner
|
||||
func (md *ScannerAdapterMetadata) HasCapability(mimeType string) bool {
|
||||
for _, capability := range md.Capabilities {
|
||||
for _, mt := range capability.ConsumesMimeTypes {
|
||||
if mt == mimeType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return slices.ContainsFunc(md.Capabilities, func(c *ScannerCapability) bool {
|
||||
return slices.Contains(c.ConsumesMimeTypes, mimeType)
|
||||
})
|
||||
}
|
||||
|
||||
// GetCapability returns capability for the mime type
|
||||
func (md *ScannerAdapterMetadata) GetCapability(mimeType string) *ScannerCapability {
|
||||
for _, capability := range md.Capabilities {
|
||||
for _, mt := range capability.ConsumesMimeTypes {
|
||||
if mt == mimeType {
|
||||
return capability
|
||||
}
|
||||
if slices.Contains(capability.ConsumesMimeTypes, mimeType) {
|
||||
return capability
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -206,9 +207,7 @@ func (mgr *systemArtifactManager) Cleanup(ctx context.Context) (int64, int64, er
|
|||
// iterate through this copy to invoke the cleanup
|
||||
registeredCriteria := make(map[string]Selector, 0)
|
||||
mgr.lock.Lock()
|
||||
for key, val := range mgr.cleanupCriteria {
|
||||
registeredCriteria[key] = val
|
||||
}
|
||||
maps.Copy(registeredCriteria, mgr.cleanupCriteria)
|
||||
mgr.lock.Unlock()
|
||||
|
||||
for key, val := range registeredCriteria {
|
||||
|
|
|
@ -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) {
|
||||
// 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
|
||||
for i := 0; i < 5; i++ {
|
||||
for range 5 {
|
||||
statusChanged, currentStatus, retry, err := e.refreshStatus(ctx, id)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
|
|
|
@ -256,7 +256,7 @@ func (t *taskDAOTestSuite) TestGetMaxEndTime() {
|
|||
func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
|
||||
jobIDs := make([]string, 0)
|
||||
taskIDs := make([]int64, 0)
|
||||
for i := 0; i < 300; i++ {
|
||||
for i := range 300 {
|
||||
jobID := fmt.Sprintf("job-%d", i)
|
||||
tid, err := t.taskDAO.Create(t.ctx, &Task{
|
||||
JobID: jobID,
|
||||
|
@ -272,7 +272,7 @@ func (t *taskDAOTestSuite) TestUpdateStatusInBatch() {
|
|||
|
||||
err := t.taskDAO.UpdateStatusInBatch(t.ctx, jobIDs, "Stopped", 10)
|
||||
t.Require().Nil(err)
|
||||
for i := 0; i < 300; i++ {
|
||||
for i := range 300 {
|
||||
tasks, err := t.taskDAO.List(t.ctx, &q.Query{
|
||||
Keywords: q.KeyWords{"job_id": jobIDs[i]}})
|
||||
t.Require().Nil(err)
|
||||
|
|
|
@ -165,11 +165,7 @@ func (sj *SweepJob) sweep(ctx job.Context, vendorType string, retainCount int64)
|
|||
return errStop
|
||||
}
|
||||
// calculate the batch position
|
||||
j := i + sweepBatchSize
|
||||
// avoid overflow
|
||||
if j > total {
|
||||
j = total
|
||||
}
|
||||
j := min(i+sweepBatchSize, total)
|
||||
|
||||
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)
|
||||
|
|
|
@ -99,4 +99,38 @@ describe('SecurityComponent', () => {
|
|||
'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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -174,7 +174,12 @@ export class SecurityComponent implements OnInit, OnDestroy {
|
|||
this.systemAllowlist.items.forEach(item => {
|
||||
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 = {};
|
||||
cveObj.cve_id = id.trim();
|
||||
if (!map[cveObj.cve_id]) {
|
||||
|
|
|
@ -169,6 +169,46 @@ describe('ProjectPolicyConfigComponent', () => {
|
|||
.GenerateSbomOnPush
|
||||
).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', () => {
|
||||
expect(
|
||||
component.projectPolicyConfigComponent.hasChangeConfigRole
|
||||
|
|
|
@ -13,7 +13,10 @@
|
|||
// limitations under the License.
|
||||
import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
|
||||
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 { State, SystemCVEAllowlist } from '../../../../shared/services';
|
||||
import {
|
||||
|
@ -133,8 +136,8 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
|||
userProjectAllowlist = false;
|
||||
systemAllowlistOrProjectAllowlist: string;
|
||||
systemAllowlistOrProjectAllowlistOrigin: string;
|
||||
projectAllowlist;
|
||||
projectAllowlistOrigin;
|
||||
projectAllowlist: ProjectCVEAllowlist;
|
||||
projectAllowlistOrigin: ProjectCVEAllowlist;
|
||||
speedUnit = BandwidthUnit.KB;
|
||||
speedUnits = [
|
||||
{
|
||||
|
@ -454,7 +457,12 @@ export class ProjectPolicyConfigComponent implements OnInit {
|
|||
this.projectAllowlist.items.forEach(item => {
|
||||
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 = {};
|
||||
cveObj.cve_id = id.trim();
|
||||
if (!map[cveObj.cve_id]) {
|
||||
|
|
|
@ -433,6 +433,11 @@ export interface ProjectRootInterface {
|
|||
VALUE: number;
|
||||
LABEL: string;
|
||||
}
|
||||
export interface ProjectCVEAllowlist {
|
||||
id?: number;
|
||||
expires_at?: number;
|
||||
items?: Array<{ cve_id: string }>;
|
||||
}
|
||||
export interface SystemCVEAllowlist {
|
||||
id?: number;
|
||||
project_id?: number;
|
||||
|
|
|
@ -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
|
||||
// Ensure tag after head request, make sure tags in proxy cache keep update
|
||||
bCtx := orm.Context()
|
||||
for i := 0; i < ensureTagMaxRetry; i++ {
|
||||
for range ensureTagMaxRetry {
|
||||
time.Sleep(ensureTagInterval)
|
||||
bArt := lib.ArtifactInfo{ProjectName: art.ProjectName, Repository: art.Repository, Digest: string(desc.Digest)}
|
||||
err := ctl.EnsureTag(bCtx, bArt, art.Tag)
|
||||
|
|
|
@ -17,6 +17,7 @@ package vulnerable
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
|
||||
"github.com/goharbor/harbor/src/controller/artifact/processor/cnab"
|
||||
"github.com/goharbor/harbor/src/controller/artifact/processor/image"
|
||||
|
@ -110,12 +111,10 @@ func Middleware() func(http.Handler) http.Handler {
|
|||
if art.IsImageIndex() {
|
||||
// artifact is image index, skip the checking when it is in the allowlist
|
||||
skippingAllowlist := []string{image.ArtifactTypeImage, cnab.ArtifactTypeCNAB}
|
||||
for _, t := range skippingAllowlist {
|
||||
if art.Type == t {
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
if slices.Contains(skippingAllowlist, art.Type) {
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 })
|
||||
}
|
||||
|
||||
j := i + *n
|
||||
|
||||
if j >= len(items) {
|
||||
j = len(items)
|
||||
}
|
||||
|
||||
j := min(i+*n, len(items))
|
||||
result := items[i:j]
|
||||
|
||||
nextLast := ""
|
||||
|
|
|
@ -17,6 +17,7 @@ package handler
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -795,13 +796,12 @@ func (a *projectAPI) validateProjectReq(ctx context.Context, req *models.Project
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to get the registry %d: %v", *req.RegistryID, err)
|
||||
}
|
||||
|
||||
permitted := false
|
||||
for _, t := range config.GetPermittedRegistryTypesForProxyCache() {
|
||||
if string(registry.Type) == t {
|
||||
permitted = true
|
||||
break
|
||||
}
|
||||
if slices.Contains(config.GetPermittedRegistryTypesForProxyCache(), string(registry.Type)) {
|
||||
permitted = true
|
||||
}
|
||||
|
||||
if !permitted {
|
||||
return errors.BadRequestError(fmt.Errorf("unsupported registry type %s", string(registry.Type)))
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ func parseScanReportMimeTypes(header *string) []string {
|
|||
var mimeTypes []string
|
||||
|
||||
if header != nil {
|
||||
for _, mimeType := range strings.Split(*header, ",") {
|
||||
for mimeType := range strings.SplitSeq(*header, ",") {
|
||||
mimeType = strings.TrimSpace(mimeType)
|
||||
switch mimeType {
|
||||
case v1.MimeTypeNativeReport, v1.MimeTypeGenericVulnerabilityReport:
|
||||
|
|
|
@ -3,6 +3,7 @@ package job
|
|||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"slices"
|
||||
|
||||
"github.com/goharbor/harbor/src/common/http"
|
||||
"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 {
|
||||
for _, u := range mjc.JobUUID {
|
||||
if uuid == u {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains(mjc.JobUUID, uuid)
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ func OnAnything(obj any, methodName string) *mock.Call {
|
|||
}
|
||||
|
||||
args := []any{}
|
||||
for i := 0; i < fnType.NumIn(); i++ {
|
||||
for range fnType.NumIn() {
|
||||
args = append(args, mock.Anything)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
HARBOR_SRC_FOLDER=$(realpath ../../)
|
||||
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
|
||||
|
||||
|
|
|
@ -17,6 +17,12 @@ Documentation This resource provides helper functions for docker operations
|
|||
Library OperatingSystem
|
||||
Library Process
|
||||
|
||||
*** Variables ***
|
||||
# Define variables for start dockerd within private network
|
||||
${DOCKERD_CMD} dockerd
|
||||
${DOCKERD_ARGS} --iptables=false
|
||||
${LOG_FILE} ./docker-daemon.log
|
||||
|
||||
*** Keywords ***
|
||||
Run Docker Info
|
||||
[Arguments] ${docker-params}
|
||||
|
@ -115,12 +121,22 @@ Get Container IP
|
|||
# docker:1.13-dind
|
||||
# If you are running this keyword in a container, make sure it is run with --privileged turned on
|
||||
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
|
||||
#${rc} ${output}= Run And Return Rc And Output ./tests/robot-cases/Group0-Util/docker_config.sh
|
||||
#Log ${output}
|
||||
#Should Be Equal As Integers ${rc} 0
|
||||
Return From Keyword If '${pid}' != '${EMPTY}'
|
||||
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
|
||||
Process Should Be Running ${handle}
|
||||
FOR ${IDX} IN RANGE 5
|
||||
|
|
|
@ -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
|
||||
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
|
||||
* Dependency injection
|
||||
* Limited scopes with unit testing.
|
||||
* Limited scope with unit testing.
|
||||
|
||||
Also:
|
||||
|
||||
* Adding you functions to the generated `configure_swagger_*.go` seems to be a burden.
|
||||
* Lack of Interface that the service implement.
|
||||
* Adding your functions to the generated `configure_swagger_*.go` seems to be a burden.
|
||||
* Lack of Interface that the service implements.
|
||||
* Complicated and custom http clients and runtime.
|
||||
|
||||
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
|
||||
|
||||
* 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 is separated from the generated code.
|
||||
|
||||
|
@ -32,8 +32,8 @@ The `restapi.Handler` (see [example](./example/restapi/configure_swagger_petstor
|
|||
a standard `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
|
||||
that support it.
|
||||
* This handler is a standard go http.Handler, so we can now use any other middleware, library, or framework
|
||||
that supports it.
|
||||
* This handler is standard, so we understand it better.
|
||||
|
||||
## 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)
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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)
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
|
||||
For example:
|
||||
|
@ -234,7 +234,7 @@ security:
|
|||
|
||||
The securityDefinitions section defines different security types that your application can handle.
|
||||
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.
|
||||
* and `basic` - user/password that should be processed.
|
||||
|
||||
|
@ -253,7 +253,7 @@ paths:
|
|||
- 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.
|
||||
|
||||
|
@ -289,14 +289,14 @@ type Config struct {
|
|||
|
||||
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
|
||||
enforcement comes to place.
|
||||
enforcement comes into play.
|
||||
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)`.
|
||||
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.
|
||||
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
|
||||
defined for the current route in the swagger.yaml we can use the code below:
|
||||
|
||||
|
|
Loading…
Reference in New Issue