mirror of
https://github.com/project-zot/zot.git
synced 2026-06-15 11:37:56 +08:00
9aff5b8d08
* chore: fix dependabot alerts Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: fix dependabot alerts Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: fix dependabot alerts Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: fix golangci-lint findings from CI Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: fix golangci-lint gosec warnings Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update code to use slices package and address gosec linting issues Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * build: fix makefile target Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update tests to use context in HTTP requests and add gosec annotations Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update tests to use context in HTTP requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update tests to use context in HTTP requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update tests to use context in HTTP requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update tests to use context in HTTP requests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: bump zui version Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: update test helpers and improve security settings in tests Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> * chore: add gosec linting directive for test path construction Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com> --------- Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
362 lines
12 KiB
Go
362 lines
12 KiB
Go
package rediscfg_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
. "github.com/smartystreets/goconvey/convey"
|
|
|
|
"zotregistry.dev/zot/v2/pkg/api/config"
|
|
rediscfg "zotregistry.dev/zot/v2/pkg/api/config/redis"
|
|
"zotregistry.dev/zot/v2/pkg/cli/server"
|
|
"zotregistry.dev/zot/v2/pkg/log"
|
|
test "zotregistry.dev/zot/v2/pkg/test/common"
|
|
)
|
|
|
|
func TestRedisLogger(t *testing.T) {
|
|
Convey("Print using Redis logger", t, func() {
|
|
logFile := test.MakeTempFile(t, "zot-log.txt")
|
|
defer logFile.Close()
|
|
|
|
writers := io.MultiWriter(os.Stdout, logFile)
|
|
logger := log.NewLoggerWithWriter("debug", writers)
|
|
|
|
rlog := rediscfg.RedisLogger{logger}
|
|
rlog.Printf(context.Background(), "this is a rest string")
|
|
|
|
content, err := os.ReadFile(logFile.Name())
|
|
So(err, ShouldBeNil)
|
|
|
|
So(string(content), ShouldContainSubstring, "this is a rest string")
|
|
})
|
|
}
|
|
|
|
func TestRedisOptions(t *testing.T) {
|
|
Convey("Test redis initialization", t, func() {
|
|
log := log.NewTestLogger()
|
|
So(log, ShouldNotBeNil)
|
|
|
|
Convey("Test redis url parsing", func() {
|
|
// Errors
|
|
config := map[string]any{"url": false}
|
|
|
|
clientIntf, err := rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(clientIntf, ShouldBeNil)
|
|
|
|
config = map[string]any{"url": ""}
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(clientIntf, ShouldBeNil)
|
|
|
|
config = map[string]any{"url": "qwerty@localhost:6379/1?dial_timeout=5s"}
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(clientIntf, ShouldBeNil)
|
|
|
|
config = map[string]any{"url": "http://:qwerty@localhost:6379/1?dial_timeout=5s"}
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(clientIntf, ShouldBeNil)
|
|
|
|
config = map[string]any{"url": "http://localhost:6379/1?addr=host2:6379&addr=host1:6379"}
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldNotBeNil)
|
|
So(clientIntf, ShouldBeNil)
|
|
|
|
// Success
|
|
config = map[string]any{"url": "redis://user:password@localhost:6379/1?dial_timeout=5s"} //nolint: gosec
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok := clientIntf.(*redis.Client)
|
|
So(ok, ShouldBeTrue)
|
|
|
|
config = map[string]any{"url": "redis://user:password@host1:6379?addr=host2:6379&addr=host1:6379"}
|
|
|
|
clientIntf, err = rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok = clientIntf.(*redis.ClusterClient)
|
|
So(ok, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("Test empty redis options from struct successfully", func() {
|
|
config := map[string]any{}
|
|
|
|
// All attributes will have zero values
|
|
options := rediscfg.ParseRedisUniversalOptions(config, log)
|
|
So(options, ShouldNotBeNil)
|
|
So(options.Addrs, ShouldEqual, []string(nil))
|
|
So(options.DB, ShouldEqual, 0)
|
|
So(options.MasterName, ShouldEqual, "")
|
|
So(options.ClientName, ShouldEqual, "")
|
|
So(options.Protocol, ShouldEqual, 0)
|
|
So(options.Username, ShouldEqual, "")
|
|
So(options.Password, ShouldEqual, "")
|
|
So(options.SentinelUsername, ShouldEqual, "")
|
|
So(options.SentinelPassword, ShouldEqual, "")
|
|
So(options.DialTimeout, ShouldEqual, 0)
|
|
So(options.MaxRetries, ShouldEqual, 0)
|
|
So(options.MinRetryBackoff, ShouldEqual, 0)
|
|
So(options.MaxRetryBackoff, ShouldEqual, 0)
|
|
So(options.ReadTimeout, ShouldEqual, 0)
|
|
So(options.WriteTimeout, ShouldEqual, 0)
|
|
So(options.ContextTimeoutEnabled, ShouldEqual, false)
|
|
So(options.PoolFIFO, ShouldEqual, false)
|
|
So(options.PoolSize, ShouldEqual, 0)
|
|
So(options.PoolTimeout, ShouldEqual, 0)
|
|
So(options.MinIdleConns, ShouldEqual, 0)
|
|
So(options.MaxIdleConns, ShouldEqual, 0)
|
|
So(options.MaxActiveConns, ShouldEqual, 0)
|
|
So(options.ConnMaxIdleTime, ShouldEqual, 0)
|
|
So(options.ConnMaxLifetime, ShouldEqual, 0)
|
|
So(options.MaxRedirects, ShouldEqual, 0)
|
|
So(options.ReadOnly, ShouldEqual, false)
|
|
So(options.RouteByLatency, ShouldEqual, false)
|
|
So(options.RouteRandomly, ShouldEqual, false)
|
|
So(options.DisableIdentity, ShouldEqual, false)
|
|
So(options.IdentitySuffix, ShouldEqual, "")
|
|
So(options.UnstableResp3, ShouldEqual, false)
|
|
|
|
clientIntf, err := rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok := clientIntf.(*redis.Client)
|
|
So(ok, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("Test redis options from struct successfully", func() {
|
|
config := map[string]any{
|
|
"addr": []string{
|
|
"a.repo:26379",
|
|
"b.repo:26379",
|
|
"c.repo:26379",
|
|
},
|
|
"db": 1,
|
|
"master_name": "zotmeta",
|
|
"client_name": "client",
|
|
"protocol": 3,
|
|
"username": "redis",
|
|
"password": "**secret**",
|
|
"sentinel_username": "sentinel",
|
|
"sentinel_password": "**secret**",
|
|
"dial_timeout": 5 * time.Second,
|
|
"max_retries": 5,
|
|
"min_retry_backoff": 1 * time.Second,
|
|
"max_retry_backoff": 3 * time.Second,
|
|
"read_timeout": 1 * time.Second,
|
|
"write_timeout": 1 * time.Second,
|
|
"context_timeout_enabled": true,
|
|
"pool_fifo": false,
|
|
"pool_size": 2,
|
|
"pool_timeout": 10 * time.Second,
|
|
"min_idle_conns": 1,
|
|
"max_idle_conns": 2,
|
|
"max_active_conns": 3,
|
|
"conn_max_idle_time": 20 * time.Second,
|
|
"conn_max_lifetime": 50 * time.Second,
|
|
"max_redirects": 3,
|
|
"read_only": true,
|
|
"route_by_latency": false,
|
|
"route_randomly": true,
|
|
"disable_identity": false,
|
|
"identity_suffix": "test",
|
|
"unstable_resp3": true,
|
|
}
|
|
|
|
// All attribute values are taken from config
|
|
options := rediscfg.ParseRedisUniversalOptions(config, log)
|
|
So(options, ShouldNotBeNil)
|
|
So(options.Addrs, ShouldEqual, []string{"a.repo:26379", "b.repo:26379", "c.repo:26379"})
|
|
So(options.DB, ShouldEqual, 1)
|
|
So(options.MasterName, ShouldEqual, "zotmeta")
|
|
So(options.ClientName, ShouldEqual, "client")
|
|
So(options.Protocol, ShouldEqual, 3)
|
|
So(options.Username, ShouldEqual, "redis")
|
|
So(options.Password, ShouldEqual, "**secret**")
|
|
So(options.SentinelUsername, ShouldEqual, "sentinel")
|
|
So(options.SentinelPassword, ShouldEqual, "**secret**")
|
|
So(options.DialTimeout, ShouldEqual, 5*time.Second)
|
|
So(options.MaxRetries, ShouldEqual, 5)
|
|
So(options.MinRetryBackoff, ShouldEqual, 1*time.Second)
|
|
So(options.MaxRetryBackoff, ShouldEqual, 3*time.Second)
|
|
So(options.ReadTimeout, ShouldEqual, 1*time.Second)
|
|
So(options.WriteTimeout, ShouldEqual, 1*time.Second)
|
|
So(options.ContextTimeoutEnabled, ShouldEqual, true)
|
|
So(options.PoolFIFO, ShouldEqual, false)
|
|
So(options.PoolSize, ShouldEqual, 2)
|
|
So(options.PoolTimeout, ShouldEqual, 10*time.Second)
|
|
So(options.MinIdleConns, ShouldEqual, 1)
|
|
So(options.MaxIdleConns, ShouldEqual, 2)
|
|
So(options.MaxActiveConns, ShouldEqual, 3)
|
|
So(options.ConnMaxIdleTime, ShouldEqual, 20*time.Second)
|
|
So(options.ConnMaxLifetime, ShouldEqual, 50*time.Second)
|
|
So(options.MaxRedirects, ShouldEqual, 3)
|
|
So(options.ReadOnly, ShouldEqual, true)
|
|
So(options.RouteByLatency, ShouldEqual, false)
|
|
So(options.RouteRandomly, ShouldEqual, true)
|
|
So(options.DisableIdentity, ShouldEqual, false)
|
|
So(options.IdentitySuffix, ShouldEqual, "test")
|
|
So(options.UnstableResp3, ShouldEqual, true)
|
|
|
|
clientIntf, err := rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok := clientIntf.(*redis.ClusterClient)
|
|
So(ok, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("Test redis options from struct with warnings", func() {
|
|
config := map[string]any{
|
|
"addr": map[string]int{},
|
|
"db": "somestring",
|
|
"master_name": map[string]int{},
|
|
"client_name": map[string]int{},
|
|
"protocol": "somestring",
|
|
"username": map[string]int{},
|
|
"password": map[string]int{},
|
|
"sentinel_username": map[string]int{},
|
|
"sentinel_password": map[string]int{},
|
|
"dial_timeout": "somestring",
|
|
"max_retries": "somestring",
|
|
"min_retry_backoff": "somestring",
|
|
"max_retry_backoff": "somestring",
|
|
"read_timeout": false,
|
|
"write_timeout": true,
|
|
"context_timeout_enabled": "somestring",
|
|
"pool_fifo": "somestring",
|
|
"pool_size": "somestring",
|
|
"pool_timeout": "somestring",
|
|
"min_idle_conns": map[string]int{},
|
|
"max_idle_conns": map[string]int{},
|
|
"max_active_conns": "somestring",
|
|
"conn_max_idle_time": "somestring",
|
|
"conn_max_lifetime": "somestring",
|
|
"max_redirects": map[string]int{},
|
|
"read_only": map[string]int{},
|
|
"route_by_latency": "somestring",
|
|
"route_randomly": map[string]int{},
|
|
"disable_identity": "somestring",
|
|
"identity_suffix": map[string]int{},
|
|
"unstable_resp3": "somestring",
|
|
}
|
|
|
|
// All attributes remain with default values
|
|
options := rediscfg.ParseRedisUniversalOptions(config, log)
|
|
So(options, ShouldNotBeNil)
|
|
So(options.Addrs, ShouldEqual, []string(nil))
|
|
So(options.DB, ShouldEqual, 0)
|
|
So(options.MasterName, ShouldEqual, "")
|
|
So(options.ClientName, ShouldEqual, "")
|
|
So(options.Protocol, ShouldEqual, 0)
|
|
So(options.Username, ShouldEqual, "")
|
|
So(options.Password, ShouldEqual, "")
|
|
So(options.SentinelUsername, ShouldEqual, "")
|
|
So(options.SentinelPassword, ShouldEqual, "")
|
|
So(options.DialTimeout, ShouldEqual, 0)
|
|
So(options.MaxRetries, ShouldEqual, 0)
|
|
So(options.MinRetryBackoff, ShouldEqual, 0)
|
|
So(options.MaxRetryBackoff, ShouldEqual, 0)
|
|
So(options.ReadTimeout, ShouldEqual, 0)
|
|
So(options.WriteTimeout, ShouldEqual, 0)
|
|
So(options.ContextTimeoutEnabled, ShouldEqual, false)
|
|
So(options.PoolFIFO, ShouldEqual, false)
|
|
So(options.PoolSize, ShouldEqual, 0)
|
|
So(options.PoolTimeout, ShouldEqual, 0)
|
|
So(options.MinIdleConns, ShouldEqual, 0)
|
|
So(options.MaxIdleConns, ShouldEqual, 0)
|
|
So(options.MaxActiveConns, ShouldEqual, 0)
|
|
So(options.ConnMaxIdleTime, ShouldEqual, 0)
|
|
So(options.ConnMaxLifetime, ShouldEqual, 0)
|
|
So(options.MaxRedirects, ShouldEqual, 0)
|
|
So(options.ReadOnly, ShouldEqual, false)
|
|
So(options.RouteByLatency, ShouldEqual, false)
|
|
So(options.RouteRandomly, ShouldEqual, false)
|
|
So(options.DisableIdentity, ShouldEqual, false)
|
|
So(options.IdentitySuffix, ShouldEqual, "")
|
|
So(options.UnstableResp3, ShouldEqual, false)
|
|
|
|
clientIntf, err := rediscfg.GetRedisClient(config, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok := clientIntf.(*redis.Client)
|
|
So(ok, ShouldBeTrue)
|
|
})
|
|
|
|
Convey("Test redis options from json", func(c C) {
|
|
fileContent := []byte(`{
|
|
"distSpecVersion": "1.1.1",
|
|
"storage": {
|
|
"remoteCache": true,
|
|
"cacheDriver": {
|
|
"name": "redis",
|
|
"addr": [
|
|
"a.repo:26379",
|
|
"b.repo:26379",
|
|
"c.repo:26379"
|
|
],
|
|
"db": 1,
|
|
"master_name": "zotmeta",
|
|
"username": "redis",
|
|
"password": "**secret**",
|
|
"dial_timeout": "5s"
|
|
},
|
|
"commit": false,
|
|
"dedupe": false,
|
|
"gc": true,
|
|
"rootDirectory": "/data/zot-cache/dev"
|
|
},
|
|
"http": {
|
|
"address": "127.0.0.1",
|
|
"port": "8080"
|
|
},
|
|
"log": {
|
|
"level": "debug"
|
|
}
|
|
}`)
|
|
|
|
dir := t.TempDir()
|
|
configPath := path.Join(dir, "test-config.json")
|
|
|
|
err := os.WriteFile(configPath, fileContent, 0o600)
|
|
So(err, ShouldBeNil)
|
|
|
|
conf := config.New()
|
|
err = server.LoadConfiguration(conf, configPath)
|
|
So(err, ShouldBeNil)
|
|
|
|
options := rediscfg.ParseRedisUniversalOptions(conf.Storage.CacheDriver, log)
|
|
So(options, ShouldNotBeNil)
|
|
So(options.Addrs, ShouldEqual, []string{"a.repo:26379", "b.repo:26379", "c.repo:26379"})
|
|
So(options.DB, ShouldEqual, 1)
|
|
So(options.MasterName, ShouldEqual, "zotmeta")
|
|
So(options.Username, ShouldEqual, "redis")
|
|
So(options.Password, ShouldEqual, "**secret**")
|
|
So(options.DialTimeout, ShouldEqual, 5*time.Second)
|
|
|
|
clientIntf, err := rediscfg.GetRedisClient(conf.Storage.CacheDriver, log)
|
|
So(err, ShouldBeNil)
|
|
So(clientIntf, ShouldNotBeNil)
|
|
|
|
_, ok := clientIntf.(*redis.Client)
|
|
So(ok, ShouldBeTrue)
|
|
})
|
|
})
|
|
}
|