Skip to content

Commit

Permalink
Merge pull request #21 from configcat/more-cache-providers
Browse files Browse the repository at this point in the history
Add support for more cache providers
  • Loading branch information
z4kn4fein authored Mar 27, 2024
2 parents 27566ea + 99b83b3 commit 73b722b
Show file tree
Hide file tree
Showing 67 changed files with 1,828 additions and 593 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/proxy-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ jobs:

test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongodb/mongodb-community-server
ports:
- 27017:27017
dynamodb:
image: amazon/dynamodb-local
ports:
- 8000:8000
steps:
- uses: actions/checkout@v4
- name: Setup Go
Expand All @@ -32,6 +41,15 @@ jobs:
coverage:
runs-on: ubuntu-latest
needs: test
services:
mongodb:
image: mongodb/mongodb-community-server
ports:
- 27017:27017
dynamodb:
image: amazon/dynamodb-local
ports:
- 8000:8000
steps:
- uses: actions/checkout@v4
with:
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/proxy-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ permissions:
jobs:
test:
runs-on: ubuntu-latest
services:
mongodb:
image: mongodb/mongodb-community-server
ports:
- 27017:27017
dynamodb:
image: amazon/dynamodb-local
ports:
- 8000:8000
steps:
- uses: actions/checkout@v4
- name: Setup Go
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21-alpine3.18 AS build
FROM golang:1.22.1-alpine3.19 AS build

WORKDIR /go/src/configcat_proxy

Expand Down
45 changes: 44 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ type GlobalOfflineConfig struct {
}

type CacheConfig struct {
Redis RedisConfig
Redis RedisConfig
MongoDb MongoDbConfig `yaml:"mongodb"`
DynamoDb DynamoDbConfig `yaml:"dynamodb"`
}

type RedisConfig struct {
Expand All @@ -172,6 +174,20 @@ type RedisConfig struct {
Tls TlsConfig
}

type MongoDbConfig struct {
Enabled bool `yaml:"enabled"`
Url string `yaml:"url"`
Database string `yaml:"database"`
Collection string `yaml:"collection"`
Tls TlsConfig
}

type DynamoDbConfig struct {
Enabled bool `yaml:"enabled"`
Url string `yaml:"url"`
Table string `yaml:"table"`
}

type LocalConfig struct {
FilePath string `yaml:"file_path"`
Polling bool `yaml:"polling"`
Expand Down Expand Up @@ -291,6 +307,11 @@ func (c *Config) setDefaults() {

c.Cache.Redis.DB = 0
c.Cache.Redis.Addresses = []string{"localhost:6379"}

c.Cache.MongoDb.Database = "configcat_proxy"
c.Cache.MongoDb.Collection = "cache"

c.Cache.DynamoDb.Table = "configcat_proxy_cache"
}

func (c *Config) fixupDefaults() {
Expand Down Expand Up @@ -366,6 +387,9 @@ func (c *Config) fixupTlsMinVersions(defVersion float64) {
if _, ok := allowedTlsVersions[c.Cache.Redis.Tls.MinVersion]; !ok {
c.Cache.Redis.Tls.MinVersion = defVersion
}
if _, ok := allowedTlsVersions[c.Cache.MongoDb.Tls.MinVersion]; !ok {
c.Cache.MongoDb.Tls.MinVersion = defVersion
}
}

func (c *Config) compileOriginRegexes() error {
Expand Down Expand Up @@ -421,6 +445,25 @@ func (k *KeepAliveConfig) ToParams() (keepalive.ServerParameters, bool) {
return param, true
}

func (c *CacheConfig) IsSet() bool {
return c.Redis.Enabled || c.MongoDb.Enabled || c.DynamoDb.Enabled
}

func (t *TlsConfig) LoadTlsOptions() (*tls.Config, error) {
conf := &tls.Config{
MinVersion: t.GetVersion(),
ServerName: t.ServerName,
}
for _, c := range t.Certificates {
if cert, err := tls.LoadX509KeyPair(c.Cert, c.Key); err == nil {
conf.Certificates = append(conf.Certificates, cert)
} else {
return nil, fmt.Errorf("failed to load certificate and key files: %s", err)
}
}
return conf, nil
}

func defaultConfigPath() (string, bool) {
switch runtime.GOOS {
case "windows":
Expand Down
184 changes: 164 additions & 20 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,18 @@ func TestConfig_Defaults(t *testing.T) {
assert.Equal(t, 0, conf.Cache.Redis.DB)
assert.Equal(t, "localhost:6379", conf.Cache.Redis.Addresses[0])

assert.Equal(t, "configcat_proxy", conf.Cache.MongoDb.Database)
assert.Equal(t, "cache", conf.Cache.MongoDb.Collection)

assert.Equal(t, "configcat_proxy_cache", conf.Cache.DynamoDb.Table)

assert.Equal(t, 1.2, conf.Tls.MinVersion)
assert.Equal(t, 1.2, conf.Cache.Redis.Tls.MinVersion)
assert.Equal(t, 1.2, conf.Cache.MongoDb.Tls.MinVersion)

assert.Equal(t, uint16(tls.VersionTLS12), conf.Tls.GetVersion())
assert.Equal(t, uint16(tls.VersionTLS12), conf.Cache.Redis.Tls.GetVersion())
assert.Equal(t, uint16(tls.VersionTLS12), conf.Cache.MongoDb.Tls.GetVersion())

assert.Nil(t, conf.DefaultAttrs)
}
Expand Down Expand Up @@ -289,7 +296,7 @@ sdks:
})
}

func TestCacheConfig_YAML(t *testing.T) {
func TestRedisConfig_YAML(t *testing.T) {
utils.UseTempFile(`
cache:
redis:
Expand Down Expand Up @@ -327,6 +334,58 @@ cache:
})
}

func TestMongoDbConfig_YAML(t *testing.T) {
utils.UseTempFile(`
cache:
mongodb:
enabled: true
url: "url"
database: "db"
collection: "coll"
tls:
enabled: true
min_version: 1.1
server_name: "serv"
certificates:
- cert: "./cert1"
key: "./key1"
- cert: "./cert2"
key: "./key2"
`, func(file string) {
conf, err := LoadConfigFromFileAndEnvironment(file)
require.NoError(t, err)

assert.True(t, conf.Cache.MongoDb.Enabled)
assert.Equal(t, "url", conf.Cache.MongoDb.Url)
assert.Equal(t, "db", conf.Cache.MongoDb.Database)
assert.Equal(t, "coll", conf.Cache.MongoDb.Collection)
assert.True(t, conf.Cache.MongoDb.Tls.Enabled)
assert.Equal(t, tls.VersionTLS11, int(conf.Cache.MongoDb.Tls.GetVersion()))
assert.Equal(t, "serv", conf.Cache.MongoDb.Tls.ServerName)
assert.Equal(t, "./cert1", conf.Cache.MongoDb.Tls.Certificates[0].Cert)
assert.Equal(t, "./key1", conf.Cache.MongoDb.Tls.Certificates[0].Key)
assert.Equal(t, "./cert2", conf.Cache.MongoDb.Tls.Certificates[1].Cert)
assert.Equal(t, "./key2", conf.Cache.MongoDb.Tls.Certificates[1].Key)
})
}

func TestDynamoDbConfig_YAML(t *testing.T) {
utils.UseTempFile(`
cache:
dynamodb:
enabled: true
url: "url"
table: "db"
`, func(file string) {
conf, err := LoadConfigFromFileAndEnvironment(file)
require.NoError(t, err)

assert.True(t, conf.Cache.DynamoDb.Enabled)
assert.Equal(t, "url", conf.Cache.DynamoDb.Url)
assert.Equal(t, "db", conf.Cache.DynamoDb.Table)
})
}

func TestGlobalOfflineConfig_YAML(t *testing.T) {
utils.UseTempFile(`
offline:
Expand Down Expand Up @@ -614,23 +673,108 @@ default_user_attributes:
}

func TestGrpcConfig_KeepAlive(t *testing.T) {
conf := KeepAliveConfig{MaxConnectionIdle: 1, MaxConnectionAge: 2, MaxConnectionAgeGrace: 3, Time: 4, Timeout: 5}
param, ok := conf.ToParams()

assert.True(t, ok)
assert.Equal(t, 1*time.Second, param.MaxConnectionIdle)
assert.Equal(t, 2*time.Second, param.MaxConnectionAge)
assert.Equal(t, 3*time.Second, param.MaxConnectionAgeGrace)
assert.Equal(t, 4*time.Second, param.Time)
assert.Equal(t, 5*time.Second, param.Timeout)

conf = KeepAliveConfig{MaxConnectionIdle: 1}
param, ok = conf.ToParams()

assert.True(t, ok)
assert.Equal(t, 1*time.Second, param.MaxConnectionIdle)
assert.Equal(t, time.Duration(0), param.MaxConnectionAge)
assert.Equal(t, time.Duration(0), param.MaxConnectionAgeGrace)
assert.Equal(t, time.Duration(0), param.Time)
assert.Equal(t, time.Duration(0), param.Timeout)
t.Run("valid", func(t *testing.T) {
conf := KeepAliveConfig{MaxConnectionIdle: 1, MaxConnectionAge: 2, MaxConnectionAgeGrace: 3, Time: 4, Timeout: 5}
param, ok := conf.ToParams()

assert.True(t, ok)
assert.Equal(t, 1*time.Second, param.MaxConnectionIdle)
assert.Equal(t, 2*time.Second, param.MaxConnectionAge)
assert.Equal(t, 3*time.Second, param.MaxConnectionAgeGrace)
assert.Equal(t, 4*time.Second, param.Time)
assert.Equal(t, 5*time.Second, param.Timeout)

conf = KeepAliveConfig{MaxConnectionIdle: 1}
param, ok = conf.ToParams()

assert.True(t, ok)
assert.Equal(t, 1*time.Second, param.MaxConnectionIdle)
assert.Equal(t, time.Duration(0), param.MaxConnectionAge)
assert.Equal(t, time.Duration(0), param.MaxConnectionAgeGrace)
assert.Equal(t, time.Duration(0), param.Time)
assert.Equal(t, time.Duration(0), param.Timeout)
})
t.Run("empty", func(t *testing.T) {
conf := KeepAliveConfig{}
_, ok := conf.ToParams()
assert.False(t, ok)
})
}

func TestTlsConfig_LoadTlsOptions(t *testing.T) {
t.Run("valid", func(t *testing.T) {
utils.UseTempFile(`
-----BEGIN CERTIFICATE-----
MIICrzCCAZcCFDnpdKF+Pg1smjtIXrNdIgxGYEJfMA0GCSqGSIb3DQEBCwUAMBQx
EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzAzMDEyMTA2NThaFw0yNDAyMjkyMTA2
NThaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAOiTDTjfAPvJLDZ2mwNvu0pohSHPRzzfZRc16iVI6+ESl0Dwjdjl
yERFO/ts1GQnhE2ggykvoxH4zUy1OCnjTJ+Mm1ryjy4G5ZIILIF9MfFcyma5/5Xd
oOTcDr3ZDTAwFaabKYKisoVMHAJCphencgoyOToW5/HRHMKOEpTJOQWSyNduXYfY
nsWb3hx7WD9NajliW7/Jjbf7UnDtKY2VM2GZWT3ygIH/7SlBqyuXJNqyZXbqfbrP
6mdZQ5wvYsnSUU4kNMtZg/ns+0H5R7PFmRhIRM0nZvJZTO9oHREdm+e2nnZwHyJF
Z26LxE7Qr1bn8+PQSydyQIqeUdaSX2LuXqECAwEAATANBgkqhkiG9w0BAQsFAAOC
AQEAjRoOTe4W4OQ6YOo5kx5sMAozh0Rg6eifS0s8GuxKwfuBop8FEnM3wAfF6x3J
fsik9MmoM4L11HWjttb46UFq/rP3GsA3DLX8i1yBOES+iyCELd5Ss9q1jfr/Jqo3
cAanE4yl3NNEZoDmMdSj2U11BneKSzHDR+l2hDF9wBifWGI9DQ1ItfA5I6MwnL+0
J03vcwPSwme4bKC/avAT2oDD7jLGLA+kuhMqHvVq7nXRzs46xyFPBBv7fBxXjPPG
c89d0ISafKtZ9kIKaRrzu2HX+b0fzKr0vtHYDLtC1U5oU7GPB12eupERkmWYlhrw
hDL3X7kt3jEZFkzGV1XL1IJx/g==
-----END CERTIFICATE-----`, func(cert string) {
utils.UseTempFile(`-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDokw043wD7ySw2
dpsDb7tKaIUhz0c832UXNeolSOvhEpdA8I3Y5chERTv7bNRkJ4RNoIMpL6MR+M1M
tTgp40yfjJta8o8uBuWSCCyBfTHxXMpmuf+V3aDk3A692Q0wMBWmmymCorKFTBwC
QqYXp3IKMjk6Fufx0RzCjhKUyTkFksjXbl2H2J7Fm94ce1g/TWo5Ylu/yY23+1Jw
7SmNlTNhmVk98oCB/+0pQasrlyTasmV26n26z+pnWUOcL2LJ0lFOJDTLWYP57PtB
+UezxZkYSETNJ2byWUzvaB0RHZvntp52cB8iRWdui8RO0K9W5/Pj0EsnckCKnlHW
kl9i7l6hAgMBAAECggEBAOMWiqeIH5a6BGCdiJhfZZmu2qd7k8xdOIDkVN7ZB/B5
TZTMDUTGgLggfgPubKfqaeW+H7N8XxZyQEtw+wjzduKm0R6JjsJbW5cuQf6htr08
ZCjP3j5/69TrBb3bjGQL32gRQwPaRsOe4A5Y84JPLivEhFoy+YEFNLbHMF905yeH
IaSeqeK0GNm0a/MU68pa1ODIc8B2zqo+f6I9qekezlDR7Or487FqnlLtNf0yvnLD
sbshzj5rzLdLYgA/RNZ4CkuGddxEYjnDB1IG0NX8m9MrHlsi7jqxa7pHt5oDrRsW
ZxBez6Q70dE29sdl5lnce3qjxweB2NK3Q6Cr2eyizwECgYEA/L/WzgY1yDMWzaCr
SRThg9NWO1EYbvz4uxt7rElfZ+NYAaT08E35Ooo9IeBzp3VoFA1PcNQnKB5pgczO
Mu5W/td5zpx1dzguBZAl4IpKkml08i06R7FxxTqtRM/P7Pna+RagtqAo3JZww3bd
ofIPH2OrobqlcFhOsLqKp5ocDNECgYEA65DJsImeBfW1aZ5ABgPr7NErSv2fKj1r
eGsgC5Za1ZiaG5LWkCpuezsvf6ma4EN3CMl5Fo617qaY6mnL2HlfVtFhHYSeLpna
9ZgqZ1zj2HkqiXOPEkb3d3cC61rXiMK97NpshrpzFx+uMCH8MMu9/CVJEHNKGgAq
6zZQ4LhjaNECgYEA3W4UeprmM2bO64d/iJ9Kk3traLw7c8EdCI+jYeVGOHXsfERQ
ctddKfRCapOBv4wUiry+hFLZm0RJmvYbEHPOs6WDiYd5QeFuMGGBTZ7ahjrtwd3t
2TGUQv6NHmQR/cNIHEG+u0DFi7whPp28vkybAx0HGMG0fyBekGZdY0iYmoECgYEA
3mVOlVYHk9ba1AEsrsErDuSXe/AgQa/E8+YnVek4jqnI7LlfyrHUppFFEcDdUFdB
XVFg+ZP4XXx5p+4EHrbP9NYuWsDm2lY1K2Livb0r+ybBqw0niPjpD6eTYQHdtOcu
ihvZFAWZPL6TJCwhvSvNjOziox5FWnDIFFKuXsqWR9ECgYAfiG1izToF+GX3yUPq
CU+ceTbM2uy3hVnQLvCnraN7hkF02Fa9ZwP6nmnsvhfdaIUP5WLm3A+qMWu/PL0i
F/dUCUF6M/DyihQUnOl+MD9Sg89ZHiftqXSY8jGR14uH4woStyUFHiFbtajmnqV7
MK4Li/LGWcksyoF+hbPNXMFCIA==
-----END PRIVATE KEY-----
`, func(key string) {
conf := TlsConfig{
MinVersion: 1.1,
ServerName: "server",
Certificates: []CertConfig{
{Key: key, Cert: cert},
},
}
tlsConf, err := conf.LoadTlsOptions()
assert.NoError(t, err)
assert.Equal(t, uint16(tls.VersionTLS11), tlsConf.MinVersion)
assert.Equal(t, "server", tlsConf.ServerName)
assert.NotEmpty(t, tlsConf.Certificates)
})
})
})
t.Run("invalid", func(t *testing.T) {
conf := TlsConfig{
MinVersion: 1.1,
ServerName: "server",
Certificates: []CertConfig{
{Key: "notexisting", Cert: "notexisting"},
},
}
tlsConf, err := conf.LoadTlsOptions()
assert.ErrorContains(t, err, "failed to load certificate and key files")
assert.Nil(t, tlsConf)
})
}
26 changes: 25 additions & 1 deletion config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ func (h *HttpProxyConfig) loadEnv(prefix string) error {

func (c *CacheConfig) loadEnv(prefix string) error {
prefix = concatPrefix(prefix, "CACHE")
return c.Redis.loadEnv(prefix)
if err := c.Redis.loadEnv(prefix); err != nil {
return err
}
if err := c.MongoDb.loadEnv(prefix); err != nil {
return err
}
return c.DynamoDb.loadEnv(prefix)
}

func (g *GlobalOfflineConfig) loadEnv(prefix string) error {
Expand Down Expand Up @@ -246,6 +252,24 @@ func (r *RedisConfig) loadEnv(prefix string) error {
return r.Tls.loadEnv(prefix)
}

func (m *MongoDbConfig) loadEnv(prefix string) error {
prefix = concatPrefix(prefix, "MONGODB")
readEnvString(prefix, "URL", &m.Url)
readEnvString(prefix, "DATABASE", &m.Database)
readEnvString(prefix, "COLLECTION", &m.Collection)
if err := readEnv(prefix, "ENABLED", &m.Enabled, toBool); err != nil {
return err
}
return m.Tls.loadEnv(prefix)
}

func (d *DynamoDbConfig) loadEnv(prefix string) error {
prefix = concatPrefix(prefix, "DYNAMODB")
readEnvString(prefix, "URL", &d.Url)
readEnvString(prefix, "TABLE", &d.Table)
return readEnv(prefix, "ENABLED", &d.Enabled, toBool)
}

func (s *SseConfig) loadEnv(prefix string) error {
prefix = concatPrefix(prefix, "SSE")
if err := readEnv(prefix, "ENABLED", &s.Enabled, toBool); err != nil {
Expand Down
Loading

0 comments on commit 73b722b

Please sign in to comment.