Skip to content

Commit

Permalink
feat(redis): redis implementation for MetaDB
Browse files Browse the repository at this point in the history
Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
  • Loading branch information
andaaron committed Jan 13, 2025
1 parent 5db6f8e commit ff5c041
Show file tree
Hide file tree
Showing 12 changed files with 2,660 additions and 158 deletions.
91 changes: 91 additions & 0 deletions pkg/api/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"testing"
"time"

"github.com/alicebob/miniredis/v2"
"github.com/google/go-github/v62/github"
"github.com/gorilla/mux"
"github.com/gorilla/securecookie"
Expand Down Expand Up @@ -154,6 +155,44 @@ func TestCreateCacheDatabaseDriver(t *testing.T) {
So(err, ShouldBeNil)
So(driver, ShouldBeNil)
})
Convey("Test CreateCacheDatabaseDriver redisdb", t, func() {
miniRedis := miniredis.RunT(t)

log := log.NewLogger("debug", "")

// fail create db, no perm
dir := t.TempDir()
conf := config.New()
conf.Storage.RootDirectory = dir
conf.Storage.Dedupe = true
conf.Storage.RemoteCache = true
conf.Storage.CacheDriver = map[string]interface{}{
"name": "redis",
"url": "redis://" + miniRedis.Addr(),
}

// test initialization for S3 storage
conf.Storage.StorageDriver = map[string]interface{}{
"name": "s3",
"rootdirectory": "/zot",
"url": "us-east-2",
}

driver, err := storage.CreateCacheDatabaseDriver(conf.Storage.StorageConfig, log)
So(err, ShouldBeNil)
So(driver, ShouldNotBeNil)
So(driver.Name(), ShouldEqual, "redis")
So(driver.UsesRelativePaths(), ShouldEqual, false)

// test initialization for local storage
conf.Storage.StorageDriver = nil

driver, err = storage.CreateCacheDatabaseDriver(conf.Storage.StorageConfig, log)
So(err, ShouldBeNil)
So(driver, ShouldNotBeNil)
So(driver.Name(), ShouldEqual, "redis")
So(driver.UsesRelativePaths(), ShouldEqual, true)
})
tskip.SkipDynamo(t)
tskip.SkipS3(t)
Convey("Test CreateCacheDatabaseDriver dynamodb", t, func() {
Expand Down Expand Up @@ -303,6 +342,58 @@ func TestCreateMetaDBDriver(t *testing.T) {
So(testFunc, ShouldNotPanic)
})

Convey("Test create MetaDB redis", t, func() {
miniRedis := miniredis.RunT(t)

log := log.NewLogger("debug", "")
dir := t.TempDir()
conf := config.New()
conf.Storage.RootDirectory = dir
conf.Storage.Dedupe = true
conf.Storage.RemoteCache = true
conf.Storage.StorageDriver = map[string]interface{}{
"name": "s3",
"rootdirectory": "/zot",
"region": "us-east-2",
"bucket": "zot-storage",
"secure": true,
"skipverify": false,
}

conf.Storage.CacheDriver = map[string]interface{}{
"name": "dummy",
}

metaDB, err := meta.New(conf.Storage.StorageConfig, log)
So(err, ShouldNotBeNil)
So(metaDB, ShouldBeNil)

conf.Storage.CacheDriver = map[string]interface{}{
"name": "redis",
}

testFunc := func() { _, _ = meta.New(conf.Storage.StorageConfig, log) }
So(testFunc, ShouldPanic)

conf.Storage.CacheDriver = map[string]interface{}{
"name": "redis",
"url": "url",
}

metaDB, err = meta.New(conf.Storage.StorageConfig, log)
So(err, ShouldNotBeNil)
So(metaDB, ShouldBeNil)

conf.Storage.CacheDriver = map[string]interface{}{
"name": "redis",
"url": "redis://" + miniRedis.Addr(),
}

metaDB, err = meta.New(conf.Storage.StorageConfig, log)
So(err, ShouldBeNil)
So(metaDB, ShouldNotBeNil)
})

Convey("Test create MetaDB bolt", t, func() {
log := log.NewLogger("debug", "")
dir := t.TempDir()
Expand Down
8 changes: 7 additions & 1 deletion pkg/extensions/extension_image_trust.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"zotregistry.dev/zot/pkg/log"
mTypes "zotregistry.dev/zot/pkg/meta/types"
"zotregistry.dev/zot/pkg/scheduler"
sconstants "zotregistry.dev/zot/pkg/storage/constants"
)

func IsBuiltWithImageTrustExtension() bool {
Expand Down Expand Up @@ -172,7 +173,11 @@ func SetupImageTrustExtension(conf *config.Config, metaDB mTypes.MetaDB, log log

var err error

if conf.Storage.RemoteCache {
if conf.Storage.RemoteCache && conf.Storage.CacheDriver["name"] == sconstants.DynamoDBDriverName {
// AWS secrets manager
// In case of AWS let's assume if dynamodDB is used, the AWS secrets manager is also used
// we use the CacheDriver settings as opposed to the storage settings because we want to
// be able to use S3/minio and redis in the same configuration
endpoint, _ := conf.Storage.CacheDriver["endpoint"].(string)
region, _ := conf.Storage.CacheDriver["region"].(string)

Expand All @@ -181,6 +186,7 @@ func SetupImageTrustExtension(conf *config.Config, metaDB mTypes.MetaDB, log log
return err
}
} else {
// Store secrets on the local disk
imgTrustStore, err = imagetrust.NewLocalImageTrustStore(conf.Storage.RootDirectory)
if err != nil {
return err
Expand Down
14 changes: 14 additions & 0 deletions pkg/extensions/extension_image_trust_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"testing"
"time"

"github.com/alicebob/miniredis/v2"
guuid "github.com/gofrs/uuid"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/generate"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
Expand Down Expand Up @@ -123,6 +124,19 @@ func TestSignatureUploadAndVerificationLocal(t *testing.T) {
})
}

func TestSignatureUploadAndVerificationRedis(t *testing.T) {
Convey("test with local storage and redis metadb", t, func() {
miniRedis := miniredis.RunT(t)

cacheDriverParams := map[string]interface{}{
"name": "redis",
"url": "redis://" + miniRedis.Addr(),
}

RunSignatureUploadAndVerificationTests(t, cacheDriverParams)
})
}

func TestSignatureUploadAndVerificationAWS(t *testing.T) {
tskip.SkipDynamo(t)

Expand Down
20 changes: 20 additions & 0 deletions pkg/extensions/imagetrust/image_trust_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"testing"
"time"

"github.com/alicebob/miniredis/v2"
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
Expand Down Expand Up @@ -653,6 +654,25 @@ func TestLocalTrustStore(t *testing.T) {
})
}

func TestLocalTrustStoreRedis(t *testing.T) {
miniRedis := miniredis.RunT(t)

Convey("test local storage and redis", t, func() {
rootDir := t.TempDir()

imageTrustStore, err := imagetrust.NewLocalImageTrustStore(rootDir)
So(err, ShouldBeNil)

dbDriverParams := map[string]interface{}{
"name": "redis",
"url": "redis://" + miniRedis.Addr(),
}

RunUploadTests(t, *imageTrustStore)
RunVerificationTests(t, dbDriverParams)
})
}

func TestAWSTrustStore(t *testing.T) {
tskip.SkipDynamo(t)

Expand Down
2 changes: 1 addition & 1 deletion pkg/meta/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func New(storageConfig config.StorageConfig, log log.Logger) (mTypes.MetaDB, err
return nil, err
}

return Create(sconstants.RedisDriverName, client, &redisdb.RedisDB{Client: client}, log) //nolint:contextcheck
return Create(sconstants.RedisDriverName, client, nil, log) //nolint:contextcheck
}

// this behavior is also mentioned in the configuration validation logic inside the cli package
Expand Down
80 changes: 77 additions & 3 deletions pkg/meta/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import (
"testing"
"time"

"github.com/alicebob/miniredis/v2"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
guuid "github.com/gofrs/uuid"
"github.com/notaryproject/notation-core-go/signature/jws"
"github.com/notaryproject/notation-go"
"github.com/notaryproject/notation-go/signer"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/redis/go-redis/v9"
. "github.com/smartystreets/goconvey/convey"

zcommon "zotregistry.dev/zot/pkg/common"
Expand All @@ -28,6 +30,7 @@ import (
"zotregistry.dev/zot/pkg/meta/boltdb"
"zotregistry.dev/zot/pkg/meta/common"
mdynamodb "zotregistry.dev/zot/pkg/meta/dynamodb"
"zotregistry.dev/zot/pkg/meta/redisdb"
mTypes "zotregistry.dev/zot/pkg/meta/types"
reqCtx "zotregistry.dev/zot/pkg/requestcontext"
tCommon "zotregistry.dev/zot/pkg/test/common"
Expand Down Expand Up @@ -164,6 +167,35 @@ func TestDynamoDBWrapper(t *testing.T) {
})
}

func TestRedisDB(t *testing.T) {
miniRedis := miniredis.RunT(t)

Convey("RedisDB Wrapper", t, func() {
rootDir := t.TempDir()
log := log.NewLogger("debug", "")

redisDriver, err := redisdb.GetRedisClient("redis://" + miniRedis.Addr())
So(err, ShouldBeNil)

metaDB, err := redisdb.New(redisDriver, log)
So(metaDB, ShouldNotBeNil)
So(err, ShouldBeNil)

imgTrustStore, err := imagetrust.NewLocalImageTrustStore(rootDir)
So(err, ShouldBeNil)

metaDB.SetImageTrustStore(imgTrustStore)

defer func() {
metaDB.ResetDB() //nolint: errcheck
os.RemoveAll(path.Join(rootDir, "_cosign"))
os.RemoveAll(path.Join(rootDir, "_notation"))
}()

RunMetaDBTests(t, metaDB)
})
}

func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func() error) { //nolint: thelper
ctx := context.Background()

Expand Down Expand Up @@ -1616,9 +1648,20 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
So(err, ShouldBeNil)
So(len(repoMetaList), ShouldEqual, 2)

So(repoMetaList[0].Tags[tag1].Digest, ShouldResemble, image1.DigestStr())
So(repoMetaList[0].Tags[tag2].Digest, ShouldResemble, image2.DigestStr())
So(repoMetaList[1].Tags[tag3].Digest, ShouldResemble, image3.DigestStr())
repos := map[string]map[string]string{}
for _, repoMeta := range repoMetaList {
if _, exists := repos[repoMeta.Name]; !exists {
repos[repoMeta.Name] = map[string]string{}
}

for tag, descriptor := range repoMeta.Tags {
repos[repoMeta.Name][tag] = descriptor.Digest
}
}

So(repos[repo1][tag1], ShouldEqual, image1.DigestStr())
So(repos[repo1][tag2], ShouldEqual, image2.DigestStr())
So(repos[repo2][tag3], ShouldEqual, image3.DigestStr())
})

Convey("Search a repo by name", func() {
Expand Down Expand Up @@ -2577,3 +2620,34 @@ func TestCreateBoltDB(t *testing.T) {
So(err, ShouldNotBeNil)
})
}

func TestCreateRedisDB(t *testing.T) {
Convey("Create", t, func() {
miniRedis := miniredis.RunT(t)

log := log.NewLogger("debug", "")
So(log, ShouldNotBeNil)

redisDriver, err := redisdb.GetRedisClient("redis://" + miniRedis.Addr())
So(err, ShouldBeNil)

metaDB, err := meta.Create("redis", redisDriver, nil, log)
So(metaDB, ShouldNotBeNil)
So(err, ShouldBeNil)
})

Convey("fails", t, func() {
log := log.NewLogger("debug", "")

_, err := meta.Create("redis", nil, mdynamodb.DBDriverParameters{}, log)
So(err, ShouldNotBeNil)

// Redis client will not be responding
redisURL := "redis://127.0.0.1:" + tCommon.GetFreePort() // must not match miniRedis.Addr()
connOpts, _ := redis.ParseURL(redisURL)
cacheDB := redis.NewClient(connOpts)

_, err = meta.Create("redis", cacheDB, nil, log)
So(err, ShouldNotBeNil)
})
}
20 changes: 20 additions & 0 deletions pkg/meta/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"testing"
"time"

"github.com/alicebob/miniredis/v2"
godigest "github.com/opencontainers/go-digest"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
. "github.com/smartystreets/goconvey/convey"
Expand All @@ -21,6 +22,7 @@ import (
"zotregistry.dev/zot/pkg/meta"
"zotregistry.dev/zot/pkg/meta/boltdb"
"zotregistry.dev/zot/pkg/meta/dynamodb"
"zotregistry.dev/zot/pkg/meta/redisdb"
mTypes "zotregistry.dev/zot/pkg/meta/types"
"zotregistry.dev/zot/pkg/storage"
"zotregistry.dev/zot/pkg/storage/local"
Expand Down Expand Up @@ -302,6 +304,24 @@ func TestParseStorageWithBoltDB(t *testing.T) {
})
}

func TestParseStorageWithRedisDB(t *testing.T) {
Convey("Redis", t, func() {
miniRedis := miniredis.RunT(t)

rootDir := t.TempDir()
log := log.NewLogger("debug", "")

redisDriver, err := redisdb.GetRedisClient("redis://" + miniRedis.Addr())
So(err, ShouldBeNil)

metaDB, err := redisdb.New(redisDriver, log)
So(metaDB, ShouldNotBeNil)
So(err, ShouldBeNil)

RunParseStorageTests(rootDir, metaDB, log)
})
}

func TestParseStorageDynamoWrapper(t *testing.T) {
tskip.SkipDynamo(t)

Expand Down
12 changes: 12 additions & 0 deletions pkg/meta/redisdb/buckets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package redisdb

// MetadataDB.
const (
ImageMetaBuck = "zot:ImageMeta"
RepoMetaBuck = "zot:RepoMeta"
RepoBlobsBuck = "zot:RepoBlobsMeta"
RepoLastUpdatedBuck = "zot:RepoLastUpdated"
UserDataBucket = "zot:UserData"
VersionBucket = "zot:Version"
UserAPIKeysBucket = "zot:UserAPIKeys" //nolint: gosec // these are not hardcoded credentials
)
Loading

0 comments on commit ff5c041

Please sign in to comment.