Skip to content

Commit 2f88d03

Browse files
authored
feat(redis): Support redis sentinel (#20)
Resolves: #19 Co-authored-by: Ziming Zhang <zziming@vmware.com> Signed-off-by: Daniel Pacak <pacak.daniel@gmail.com>
1 parent 5daec06 commit 2f88d03

File tree

15 files changed

+1048
-54
lines changed

15 files changed

+1048
-54
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@ Configuration of the adapter is done via environment variables at startup.
3434
| `SCANNER_API_SERVER_IDLE_TIMEOUT` | `60s` | The maximum amount of time to wait for the next request when keep-alives are enabled. |
3535
| `SCANNER_CLAIR_URL` | `http://harbor-harbor-clair:6060` | Clair URL |
3636
| `SCANNER_CLAIR_DATABASE_URL` | | The Clair database URL, it is used to fetch vulnerability database updated time of the Clair. Its format is `postgresql://user:password@host/db?sslmode=disable` |
37-
| `SCANNER_STORE_REDIS_URL` | `redis://harbor-harbor-redis:6379` | Redis server URI for a redis store. |
38-
| `SCANNER_STORE_REDIS_NAMESPACE` | `harbor.scanner.clair:store` | A namespace for keys in a redis store. |
39-
| `SCANNER_STORE_REDIS_POOL_MAX_ACTIVE` | `5` | The max number of connections allocated by the pool for a redis store. |
40-
| `SCANNER_STORE_REDIS_POOL_MAX_IDLE` | `5` | The max number of idle connections in the pool for a redis store. |
41-
| `SCANNER_STORE_REDIS_SCAN_JOB_TTL` | `1h` | The time to live for persisting scan jobs and associated scan reports. |
37+
| `SCANNER_STORE_REDIS_URL` | `redis://harbor-harbor-redis:6379` | Redis server URI for a Redis store. The URI supports schemas to connect to a standalone Redis server, i.e. `redis://user:password@standalone_host:port/db-number` and Redis Sentinel deployment, i.e. `redis+sentinel://user:password@sentinel_host1:port1,sentinel_host2:port2/monitor-name/db-number`. |
38+
| `SCANNER_STORE_REDIS_POOL_MAX_ACTIVE` | `5` | The max number of connections allocated by the pool for a Redis store. |
39+
| `SCANNER_STORE_REDIS_POOL_MAX_IDLE` | `5` | The max number of idle connections in the pool for a Redis store. |
40+
| `SCANNER_STORE_REDIS_POOL_IDLE_TIMEOUT` | `5m` | Close connections after remaining idle for this duration. |
41+
| `SCANNER_STORE_REDIS_POOL_CONNECTION_TIMEOUT` | `1s` | The timeout for connecting to the Redis server. |
42+
| `SCANNER_STORE_REDIS_POOL_READ_TIMEOUT` | `1s` | The timeout for reading a single Redis command reply. |
43+
| `SCANNER_STORE_REDIS_POOL_WRITE_TIMEOUT` | `1s` | The timeout for writing a single Redis command. |
44+
| `SCANNER_STORE_REDIS_NAMESPACE` | `harbor.scanner.clair:store` | A namespace for keys in a redis store. |
45+
| `SCANNER_STORE_REDIS_SCAN_JOB_TTL` | `1h` | The time to live for persisting scan jobs and associated scan reports. |
4246

4347
## Deploy to minikube
4448

cmd/harbor-scanner-clair/main.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package main
22

33
import (
44
"context"
5+
"fmt"
56
"os"
67
"os/signal"
78
"syscall"
89

10+
"github.com/goharbor/harbor-scanner-clair/pkg/redisx"
11+
912
"github.com/goharbor/harbor-scanner-clair/pkg/clair"
1013
"github.com/goharbor/harbor-scanner-clair/pkg/etc"
1114
"github.com/goharbor/harbor-scanner-clair/pkg/http/api"
@@ -30,12 +33,25 @@ func main() {
3033
log.SetReportCaller(false)
3134
log.SetFormatter(&log.JSONFormatter{})
3235

36+
if err := run(); err != nil {
37+
log.Fatalf("Error: %v", err)
38+
}
39+
}
40+
41+
func run() (err error) {
3342
config, err := etc.GetConfig()
3443
if err != nil {
35-
log.Fatalf("Error: %v", err)
44+
err = fmt.Errorf("getting config: %v", err)
45+
return
3646
}
3747

38-
store := redis.NewStore(config.Store)
48+
pool, err := redisx.NewPool(config.RedisPool)
49+
if err != nil {
50+
err = fmt.Errorf("constructing connection pool: %v", err)
51+
return
52+
}
53+
54+
store := redis.NewStore(pool, config.RedisStore)
3955

4056
workPool := work.New()
4157

@@ -49,7 +65,8 @@ func main() {
4965

5066
clairClient, err := clair.NewClient(config.TLS, config.Clair)
5167
if err != nil {
52-
log.Fatalf("Error: %v", err)
68+
err = fmt.Errorf("constructing clair client: %v", clairClient)
69+
return
5370
}
5471

5572
adapter := scanner.NewAdapter(registryClientFactory, clairClient, scanner.NewTransformer())
@@ -69,6 +86,7 @@ func main() {
6986

7087
apiServer.Shutdown(context.Background())
7188
workPool.Shutdown()
89+
_ = pool.Close()
7290

7391
close(shutdownComplete)
7492
}()
@@ -77,4 +95,5 @@ func main() {
7795
apiServer.ListenAndServe()
7896

7997
<-shutdownComplete
98+
return
8099
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/goharbor/harbor-scanner-clair
33
go 1.13
44

55
require (
6+
github.com/FZambia/sentinel v1.1.0
67
github.com/Microsoft/hcsshim v0.8.6 // indirect
78
github.com/caarlos0/env/v6 v6.0.0
89
github.com/docker/distribution v2.7.1+incompatible

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
22
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
33
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
4+
github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8=
5+
github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
46
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
57
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
68
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=

pkg/etc/config.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import (
1616
)
1717

1818
type Config struct {
19-
API APIConfig
20-
TLS TLSConfig
21-
Clair ClairConfig
22-
Store Store
19+
API APIConfig
20+
TLS TLSConfig
21+
Clair ClairConfig
22+
RedisPool RedisPool
23+
RedisStore RedisStore
2324
}
2425

2526
type APIConfig struct {
@@ -47,12 +48,19 @@ type ClairConfig struct {
4748
DatabaseURL string `env:"SCANNER_CLAIR_DATABASE_URL"`
4849
}
4950

50-
type Store struct {
51-
RedisURL string `env:"SCANNER_STORE_REDIS_URL" envDefault:"redis://harbor-harbor-redis:6379"`
52-
Namespace string `env:"SCANNER_STORE_REDIS_NAMESPACE" envDefault:"harbor.scanner.clair:store"`
53-
PoolMaxActive int `env:"SCANNER_STORE_REDIS_POOL_MAX_ACTIVE" envDefault:"5"`
54-
PoolMaxIdle int `env:"SCANNER_STORE_REDIS_POOL_MAX_IDLE" envDefault:"5"`
55-
ScanJobTTL time.Duration `env:"SCANNER_STORE_REDIS_SCAN_JOB_TTL" envDefault:"1h"`
51+
type RedisPool struct {
52+
URL string `env:"SCANNER_STORE_REDIS_URL" envDefault:"redis://harbor-harbor-redis:6379"`
53+
MaxActive int `env:"SCANNER_STORE_REDIS_POOL_MAX_ACTIVE" envDefault:"5"`
54+
MaxIdle int `env:"SCANNER_STORE_REDIS_POOL_MAX_IDLE" envDefault:"5"`
55+
IdleTimeout time.Duration `env:"SCANNER_STORE_REDIS_POOL_IDLE_TIMEOUT" envDefault:"5m"`
56+
ConnectionTimeout time.Duration `env:"SCANNER_STORE_REDIS_POOL_CONNECTION_TIMEOUT" envDefault:"1s"`
57+
ReadTimeout time.Duration `env:"SCANNER_STORE_REDIS_POOL_READ_TIMEOUT" envDefault:"1s"`
58+
WriteTimeout time.Duration `env:"SCANNER_STORE_REDIS_POOL_WRITE_TIMEOUT" envDefault:"1s"`
59+
}
60+
61+
type RedisStore struct {
62+
Namespace string `env:"SCANNER_STORE_REDIS_NAMESPACE" envDefault:"harbor.scanner.clair:store"`
63+
ScanJobTTL time.Duration `env:"SCANNER_STORE_REDIS_SCAN_JOB_TTL" envDefault:"1h"`
5664
}
5765

5866
func GetLogLevel() logrus.Level {

pkg/etc/config_test.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package etc
22

33
import (
4-
"github.com/sirupsen/logrus"
5-
"github.com/stretchr/testify/assert"
6-
"github.com/stretchr/testify/require"
74
"os"
85
"testing"
96
"time"
7+
8+
"github.com/sirupsen/logrus"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
1011
)
1112

1213
type Envs map[string]string
@@ -66,12 +67,18 @@ func TestGetConfig(t *testing.T) {
6667
Clair: ClairConfig{
6768
URL: "http://harbor-harbor-clair:6060",
6869
},
69-
Store: Store{
70-
RedisURL: "redis://harbor-harbor-redis:6379",
71-
Namespace: "harbor.scanner.clair:store",
72-
PoolMaxActive: 5,
73-
PoolMaxIdle: 5,
74-
ScanJobTTL: parseDuration(t, "1h"),
70+
RedisStore: RedisStore{
71+
Namespace: "harbor.scanner.clair:store",
72+
ScanJobTTL: parseDuration(t, "1h"),
73+
},
74+
RedisPool: RedisPool{
75+
URL: "redis://harbor-harbor-redis:6379",
76+
MaxActive: 5,
77+
MaxIdle: 5,
78+
IdleTimeout: parseDuration(t, "5m"),
79+
ConnectionTimeout: parseDuration(t, "1s"),
80+
ReadTimeout: parseDuration(t, "1s"),
81+
WriteTimeout: parseDuration(t, "1s"),
7582
},
7683
},
7784
},
@@ -89,6 +96,9 @@ func TestGetConfig(t *testing.T) {
8996
"SCANNER_TLS_CLIENTCAS": "test/data/ca.crt",
9097

9198
"SCANNER_CLAIR_URL": "https://demo.clair:7080",
99+
100+
"SCANNER_STORE_REDIS_POOL_MAX_ACTIVE": "3",
101+
"SCANNER_STORE_REDIS_POOL_MAX_IDLE": "10",
92102
},
93103
expectedConfig: Config{
94104
API: APIConfig{
@@ -105,12 +115,18 @@ func TestGetConfig(t *testing.T) {
105115
Clair: ClairConfig{
106116
URL: "https://demo.clair:7080",
107117
},
108-
Store: Store{
109-
RedisURL: "redis://harbor-harbor-redis:6379",
110-
Namespace: "harbor.scanner.clair:store",
111-
PoolMaxActive: 5,
112-
PoolMaxIdle: 5,
113-
ScanJobTTL: parseDuration(t, "1h"),
118+
RedisStore: RedisStore{
119+
Namespace: "harbor.scanner.clair:store",
120+
ScanJobTTL: parseDuration(t, "1h"),
121+
},
122+
RedisPool: RedisPool{
123+
URL: "redis://harbor-harbor-redis:6379",
124+
MaxActive: 3,
125+
MaxIdle: 10,
126+
IdleTimeout: parseDuration(t, "5m"),
127+
ConnectionTimeout: parseDuration(t, "1s"),
128+
ReadTimeout: parseDuration(t, "1s"),
129+
WriteTimeout: parseDuration(t, "1s"),
114130
},
115131
},
116132
},
@@ -124,7 +140,8 @@ func TestGetConfig(t *testing.T) {
124140
require.NoError(t, err)
125141
assert.Equal(t, tc.expectedConfig.API, cfg.API)
126142
assert.Equal(t, tc.expectedConfig.Clair, cfg.Clair)
127-
assert.Equal(t, tc.expectedConfig.Store, cfg.Store)
143+
assert.Equal(t, tc.expectedConfig.RedisStore, cfg.RedisStore)
144+
assert.Equal(t, tc.expectedConfig.RedisPool, cfg.RedisPool)
128145
})
129146
}
130147

pkg/persistence/redis/store.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package redis
33
import (
44
"encoding/json"
55
"fmt"
6+
67
"github.com/goharbor/harbor-scanner-clair/pkg/etc"
78
"github.com/goharbor/harbor-scanner-clair/pkg/harbor"
89
"github.com/goharbor/harbor-scanner-clair/pkg/job"
@@ -13,21 +14,14 @@ import (
1314
)
1415

1516
type store struct {
16-
cfg etc.Store
17-
pool redis.Pool
17+
pool *redis.Pool
18+
cfg etc.RedisStore
1819
}
1920

20-
func NewStore(cfg etc.Store) persistence.Store {
21+
func NewStore(pool *redis.Pool, cfg etc.RedisStore) persistence.Store {
2122
return &store{
22-
cfg: cfg,
23-
pool: redis.Pool{
24-
Dial: func() (redis.Conn, error) {
25-
return redis.DialURL(cfg.RedisURL)
26-
},
27-
MaxIdle: cfg.PoolMaxIdle,
28-
MaxActive: cfg.PoolMaxActive,
29-
Wait: true,
30-
},
23+
cfg: cfg,
24+
pool: pool,
3125
}
3226
}
3327

0 commit comments

Comments
 (0)