Standardize metadata parsing in configstores and lockstore (#2710)
Signed-off-by: Bernd Verst <github@bernd.dev>
This commit is contained in:
parent
3dc2691635
commit
2266e7ad44
|
@ -17,7 +17,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
"github.com/dapr/components-contrib/configuration"
|
||||
azauth "github.com/dapr/components-contrib/internal/authentication/azure"
|
||||
contribMetadata "github.com/dapr/components-contrib/metadata"
|
||||
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
@ -85,9 +86,9 @@ func (r *ConfigurationStore) Init(_ context.Context, metadata configuration.Meta
|
|||
ApplicationID: "dapr-" + logger.DaprVersion,
|
||||
},
|
||||
Retry: policy.RetryOptions{
|
||||
MaxRetries: int32(m.maxRetries),
|
||||
RetryDelay: m.maxRetryDelay,
|
||||
MaxRetryDelay: m.maxRetryDelay,
|
||||
MaxRetries: int32(m.MaxRetries),
|
||||
RetryDelay: m.internalMaxRetryDelay,
|
||||
MaxRetryDelay: m.internalMaxRetryDelay,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -95,8 +96,8 @@ func (r *ConfigurationStore) Init(_ context.Context, metadata configuration.Meta
|
|||
ClientOptions: coreClientOpts,
|
||||
}
|
||||
|
||||
if r.metadata.connectionString != "" {
|
||||
r.client, err = azappconfig.NewClientFromConnectionString(r.metadata.connectionString, &options)
|
||||
if r.metadata.ConnectionString != "" {
|
||||
r.client, err = azappconfig.NewClientFromConnectionString(r.metadata.ConnectionString, &options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -113,7 +114,7 @@ func (r *ConfigurationStore) Init(_ context.Context, metadata configuration.Meta
|
|||
return err
|
||||
}
|
||||
|
||||
r.client, err = azappconfig.NewClient(r.metadata.host, cred, &options)
|
||||
r.client, err = azappconfig.NewClient(r.metadata.Host, cred, &options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -123,67 +124,37 @@ func (r *ConfigurationStore) Init(_ context.Context, metadata configuration.Meta
|
|||
}
|
||||
|
||||
func parseMetadata(meta configuration.Metadata) (metadata, error) {
|
||||
m := metadata{}
|
||||
|
||||
if val, ok := meta.Properties[host]; ok && val != "" {
|
||||
m.host = val
|
||||
m := metadata{
|
||||
MaxRetries: defaultMaxRetries,
|
||||
internalMaxRetryDelay: defaultMaxRetryDelay,
|
||||
internalRetryDelay: defaultRetryDelay,
|
||||
internalSubscribePollInterval: defaultSubscribePollInterval,
|
||||
internalRequestTimeout: defaultRequestTimeout,
|
||||
}
|
||||
decodeErr := contribMetadata.DecodeMetadata(meta.Properties, &m)
|
||||
if decodeErr != nil {
|
||||
return m, decodeErr
|
||||
}
|
||||
|
||||
if val, ok := meta.Properties[connectionString]; ok && val != "" {
|
||||
m.connectionString = val
|
||||
}
|
||||
|
||||
if m.connectionString != "" && m.host != "" {
|
||||
if m.ConnectionString != "" && m.Host != "" {
|
||||
return m, fmt.Errorf("azure appconfig error: can't set both %s and %s fields in metadata", host, connectionString)
|
||||
}
|
||||
|
||||
if m.connectionString == "" && m.host == "" {
|
||||
if m.ConnectionString == "" && m.Host == "" {
|
||||
return m, fmt.Errorf("azure appconfig error: specify %s or %s field in metadata", host, connectionString)
|
||||
}
|
||||
|
||||
m.maxRetries = defaultMaxRetries
|
||||
if val, ok := meta.Properties[maxRetries]; ok && val != "" {
|
||||
parsedVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("azure appconfig error: can't parse maxRetries field: %w", err)
|
||||
}
|
||||
m.maxRetries = parsedVal
|
||||
if m.MaxRetryDelay != nil {
|
||||
m.internalMaxRetryDelay = time.Duration(*m.MaxRetryDelay)
|
||||
}
|
||||
|
||||
m.maxRetryDelay = defaultMaxRetryDelay
|
||||
if val, ok := meta.Properties[maxRetryDelay]; ok && val != "" {
|
||||
parsedVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("azure appconfig error: can't parse maxRetryDelay field: %w", err)
|
||||
}
|
||||
m.maxRetryDelay = time.Duration(parsedVal)
|
||||
if m.RetryDelay != nil {
|
||||
m.internalRetryDelay = time.Duration(*m.RetryDelay)
|
||||
}
|
||||
|
||||
m.retryDelay = defaultRetryDelay
|
||||
if val, ok := meta.Properties[retryDelay]; ok && val != "" {
|
||||
parsedVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("azure appconfig error: can't parse retryDelay field: %w", err)
|
||||
}
|
||||
m.retryDelay = time.Duration(parsedVal)
|
||||
if m.SubscribePollInterval != nil {
|
||||
m.internalSubscribePollInterval = time.Duration(*m.SubscribePollInterval)
|
||||
}
|
||||
|
||||
m.subscribePollInterval = defaultSubscribePollInterval
|
||||
if val, ok := meta.Properties[subscribePollInterval]; ok && val != "" {
|
||||
parsedVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("azure appconfig error: can't parse subscribePollInterval field: %w", err)
|
||||
}
|
||||
m.subscribePollInterval = time.Duration(parsedVal)
|
||||
}
|
||||
|
||||
m.requestTimeout = defaultRequestTimeout
|
||||
if val, ok := meta.Properties[requestTimeout]; ok && val != "" {
|
||||
parsedVal, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("azure appconfig error: can't parse requestTimeout field: %w", err)
|
||||
}
|
||||
m.requestTimeout = time.Duration(parsedVal)
|
||||
if m.RequestTimeout != nil {
|
||||
m.internalRequestTimeout = time.Duration(*m.RequestTimeout)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
|
@ -245,7 +216,7 @@ func (r *ConfigurationStore) getAll(ctx context.Context, req *configuration.GetR
|
|||
nil)
|
||||
|
||||
for allSettingsPgr.More() {
|
||||
timeoutContext, cancel := context.WithTimeout(ctx, r.metadata.requestTimeout)
|
||||
timeoutContext, cancel := context.WithTimeout(ctx, r.metadata.internalRequestTimeout)
|
||||
defer cancel()
|
||||
if revResp, err := allSettingsPgr.NextPage(timeoutContext); err == nil {
|
||||
for _, setting := range revResp.Settings {
|
||||
|
@ -326,13 +297,13 @@ func (r *ConfigurationStore) doSubscribe(ctx context.Context, req *configuration
|
|||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(r.metadata.subscribePollInterval):
|
||||
case <-time.After(r.metadata.internalSubscribePollInterval):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ConfigurationStore) getSettings(ctx context.Context, key string, getSettingsOptions *azappconfig.GetSettingOptions) (azappconfig.GetSettingResponse, error) {
|
||||
timeoutContext, cancel := context.WithTimeout(ctx, r.metadata.requestTimeout)
|
||||
timeoutContext, cancel := context.WithTimeout(ctx, r.metadata.internalRequestTimeout)
|
||||
defer cancel()
|
||||
resp, err := r.client.GetSetting(timeoutContext, key, getSettingsOptions)
|
||||
return resp, err
|
||||
|
@ -365,3 +336,11 @@ func (r *ConfigurationStore) Unsubscribe(ctx context.Context, req *configuration
|
|||
}
|
||||
return fmt.Errorf("azure appconfig error: subscription with id %s does not exist", req.ID)
|
||||
}
|
||||
|
||||
// GetComponentMetadata returns the metadata of the component.
|
||||
func (r *ConfigurationStore) GetComponentMetadata() map[string]string {
|
||||
metadataStruct := metadata{}
|
||||
metadataInfo := map[string]string{}
|
||||
contribMetadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo)
|
||||
return metadataInfo
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ package appconfig
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -206,12 +205,12 @@ func TestInit(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
cs, ok := s.(*ConfigurationStore)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, testProperties[host], cs.metadata.host)
|
||||
assert.Equal(t, 3, cs.metadata.maxRetries)
|
||||
assert.Equal(t, time.Second*4, cs.metadata.retryDelay)
|
||||
assert.Equal(t, time.Second*120, cs.metadata.maxRetryDelay)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.subscribePollInterval)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.requestTimeout)
|
||||
assert.Equal(t, testProperties[host], cs.metadata.Host)
|
||||
assert.Equal(t, 3, cs.metadata.MaxRetries)
|
||||
assert.Equal(t, time.Second*4, cs.metadata.internalRetryDelay)
|
||||
assert.Equal(t, time.Second*120, cs.metadata.internalMaxRetryDelay)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.internalSubscribePollInterval)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.internalRequestTimeout)
|
||||
})
|
||||
|
||||
t.Run("Init with valid appConfigConnectionString metadata", func(t *testing.T) {
|
||||
|
@ -231,12 +230,12 @@ func TestInit(t *testing.T) {
|
|||
assert.Nil(t, err)
|
||||
cs, ok := s.(*ConfigurationStore)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, testProperties[connectionString], cs.metadata.connectionString)
|
||||
assert.Equal(t, 3, cs.metadata.maxRetries)
|
||||
assert.Equal(t, time.Second*4, cs.metadata.retryDelay)
|
||||
assert.Equal(t, time.Second*120, cs.metadata.maxRetryDelay)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.subscribePollInterval)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.requestTimeout)
|
||||
assert.Equal(t, testProperties[connectionString], cs.metadata.ConnectionString)
|
||||
assert.Equal(t, 3, cs.metadata.MaxRetries)
|
||||
assert.Equal(t, time.Second*4, cs.metadata.internalRetryDelay)
|
||||
assert.Equal(t, time.Second*120, cs.metadata.internalMaxRetryDelay)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.internalSubscribePollInterval)
|
||||
assert.Equal(t, time.Second*30, cs.metadata.internalRequestTimeout)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -255,19 +254,22 @@ func Test_parseMetadata(t *testing.T) {
|
|||
}}
|
||||
|
||||
want := metadata{
|
||||
host: "testHost",
|
||||
maxRetries: 3,
|
||||
retryDelay: time.Second * 4,
|
||||
maxRetryDelay: time.Second * 120,
|
||||
subscribePollInterval: time.Second * 30,
|
||||
requestTimeout: time.Second * 30,
|
||||
Host: "testHost",
|
||||
MaxRetries: 3,
|
||||
internalRetryDelay: time.Second * 4,
|
||||
internalMaxRetryDelay: time.Second * 120,
|
||||
internalSubscribePollInterval: time.Second * 30,
|
||||
internalRequestTimeout: time.Second * 30,
|
||||
}
|
||||
|
||||
m, _ := parseMetadata(meta)
|
||||
assert.NotNil(t, m)
|
||||
if !reflect.DeepEqual(m, want) {
|
||||
t.Errorf("parseMetadata() got = %v, want %v", m, want)
|
||||
}
|
||||
assert.Equal(t, want.Host, m.Host)
|
||||
assert.Equal(t, want.MaxRetries, m.MaxRetries)
|
||||
assert.Equal(t, want.internalRetryDelay, m.internalRetryDelay)
|
||||
assert.Equal(t, want.internalMaxRetryDelay, m.internalMaxRetryDelay)
|
||||
assert.Equal(t, want.internalSubscribePollInterval, m.internalSubscribePollInterval)
|
||||
assert.Equal(t, want.internalRequestTimeout, m.internalRequestTimeout)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("parse metadata with %s", connectionString), func(t *testing.T) {
|
||||
|
@ -284,19 +286,22 @@ func Test_parseMetadata(t *testing.T) {
|
|||
}}
|
||||
|
||||
want := metadata{
|
||||
connectionString: "testConnectionString",
|
||||
maxRetries: 3,
|
||||
retryDelay: time.Second * 4,
|
||||
maxRetryDelay: time.Second * 120,
|
||||
subscribePollInterval: time.Second * 30,
|
||||
requestTimeout: time.Second * 30,
|
||||
ConnectionString: "testConnectionString",
|
||||
MaxRetries: 3,
|
||||
internalRetryDelay: time.Second * 4,
|
||||
internalMaxRetryDelay: time.Second * 120,
|
||||
internalSubscribePollInterval: time.Second * 30,
|
||||
internalRequestTimeout: time.Second * 30,
|
||||
}
|
||||
|
||||
m, _ := parseMetadata(meta)
|
||||
assert.NotNil(t, m)
|
||||
if !reflect.DeepEqual(m, want) {
|
||||
t.Errorf("parseMetadata() got = %v, want %v", m, want)
|
||||
}
|
||||
assert.Equal(t, want.ConnectionString, m.ConnectionString)
|
||||
assert.Equal(t, want.MaxRetries, m.MaxRetries)
|
||||
assert.Equal(t, want.internalRetryDelay, m.internalRetryDelay)
|
||||
assert.Equal(t, want.internalMaxRetryDelay, m.internalMaxRetryDelay)
|
||||
assert.Equal(t, want.internalSubscribePollInterval, m.internalSubscribePollInterval)
|
||||
assert.Equal(t, want.internalRequestTimeout, m.internalRequestTimeout)
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("both %s and %s fields set in metadata", host, connectionString), func(t *testing.T) {
|
||||
|
|
|
@ -16,11 +16,16 @@ package appconfig
|
|||
import "time"
|
||||
|
||||
type metadata struct {
|
||||
host string
|
||||
connectionString string
|
||||
maxRetries int
|
||||
maxRetryDelay time.Duration
|
||||
retryDelay time.Duration
|
||||
subscribePollInterval time.Duration
|
||||
requestTimeout time.Duration
|
||||
Host string `mapstructure:"host"`
|
||||
ConnectionString string `mapstructure:"connectionString"`
|
||||
MaxRetries int `mapstructure:"maxRetries"`
|
||||
MaxRetryDelay *int `mapstructure:"maxRetryDelay"`
|
||||
RetryDelay *int `mapstructure:"retryDelay"`
|
||||
SubscribePollInterval *int `mapstructure:"subscribePollInterval"`
|
||||
RequestTimeout *int `mapstructure:"requestTimeout"`
|
||||
|
||||
internalRequestTimeout time.Duration `mapstructure:"-"`
|
||||
internalMaxRetryDelay time.Duration `mapstructure:"-"`
|
||||
internalSubscribePollInterval time.Duration `mapstructure:"-"`
|
||||
internalRetryDelay time.Duration `mapstructure:"-"`
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package postgres
|
|||
import "time"
|
||||
|
||||
type metadata struct {
|
||||
maxIdleTimeout time.Duration
|
||||
connectionString string
|
||||
configTable string
|
||||
MaxIdleTimeout time.Duration `mapstructure:"connMaxIdleTime"`
|
||||
ConnectionString string `mapstructure:"connectionString"`
|
||||
ConfigTable string `mapstructure:"table"`
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"k8s.io/utils/strings/slices"
|
||||
|
||||
"github.com/dapr/components-contrib/configuration"
|
||||
contribMetadata "github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
|
@ -98,9 +99,9 @@ func (p *ConfigurationStore) Init(parentCtx context.Context, metadata configurat
|
|||
p.metadata = m
|
||||
}
|
||||
p.ActiveSubscriptions = make(map[string]*subscription)
|
||||
ctx, cancel := context.WithTimeout(parentCtx, p.metadata.maxIdleTimeout)
|
||||
ctx, cancel := context.WithTimeout(parentCtx, p.metadata.MaxIdleTimeout)
|
||||
defer cancel()
|
||||
client, err := Connect(ctx, p.metadata.connectionString, p.metadata.maxIdleTimeout)
|
||||
client, err := Connect(ctx, p.metadata.ConnectionString, p.metadata.MaxIdleTimeout)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error connecting to configuration store: '%w'", err)
|
||||
}
|
||||
|
@ -111,12 +112,12 @@ func (p *ConfigurationStore) Init(parentCtx context.Context, metadata configurat
|
|||
}
|
||||
// check if table exists
|
||||
exists := false
|
||||
err = p.client.QueryRow(ctx, QueryTableExists, p.metadata.configTable).Scan(&exists)
|
||||
err = p.client.QueryRow(ctx, QueryTableExists, p.metadata.ConfigTable).Scan(&exists)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return fmt.Errorf(ErrorMissingTable, p.metadata.configTable)
|
||||
return fmt.Errorf(ErrorMissingTable, p.metadata.ConfigTable)
|
||||
}
|
||||
return fmt.Errorf("error in checking if configtable '%s' exists - '%w'", p.metadata.configTable, err)
|
||||
return fmt.Errorf("error in checking if configtable '%s' exists - '%w'", p.metadata.ConfigTable, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -126,7 +127,7 @@ func (p *ConfigurationStore) Get(ctx context.Context, req *configuration.GetRequ
|
|||
p.logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
query, params, err := buildQuery(req, p.metadata.configTable)
|
||||
query, params, err := buildQuery(req, p.metadata.ConfigTable)
|
||||
if err != nil {
|
||||
p.logger.Error(err)
|
||||
return nil, fmt.Errorf("error in configuration store query: '%w' ", err)
|
||||
|
@ -168,7 +169,7 @@ func (p *ConfigurationStore) Subscribe(ctx context.Context, req *configuration.S
|
|||
}
|
||||
}
|
||||
if len(pgNotifyChannels) == 0 {
|
||||
return "", fmt.Errorf("unable to subscribe to '%s'.pgNotifyChannel attribute cannot be empty", p.metadata.configTable)
|
||||
return "", fmt.Errorf("unable to subscribe to '%s'.pgNotifyChannel attribute cannot be empty", p.metadata.ConfigTable)
|
||||
}
|
||||
return p.subscribeToChannel(ctx, pgNotifyChannels, req, handler)
|
||||
}
|
||||
|
@ -278,31 +279,30 @@ func (p *ConfigurationStore) handleSubscribedChange(ctx context.Context, handler
|
|||
}
|
||||
|
||||
func parseMetadata(cmetadata configuration.Metadata) (metadata, error) {
|
||||
m := metadata{}
|
||||
if val, ok := cmetadata.Properties[connectionStringKey]; ok && val != "" {
|
||||
m.connectionString = val
|
||||
} else {
|
||||
m := metadata{
|
||||
MaxIdleTimeout: defaultMaxConnIdleTime,
|
||||
}
|
||||
decodeErr := contribMetadata.DecodeMetadata(cmetadata.Properties, &m)
|
||||
if decodeErr != nil {
|
||||
return m, decodeErr
|
||||
}
|
||||
|
||||
if m.ConnectionString == "" {
|
||||
return m, fmt.Errorf(ErrorMissingConnectionString)
|
||||
}
|
||||
if tbl, ok := cmetadata.Properties[configtablekey]; ok && tbl != "" {
|
||||
if !allowedChars.MatchString(tbl) {
|
||||
return m, fmt.Errorf("invalid table name : '%v'. non-alphanumerics are not supported", tbl)
|
||||
|
||||
if m.ConfigTable != "" {
|
||||
if !allowedChars.MatchString(m.ConfigTable) {
|
||||
return m, fmt.Errorf("invalid table name : '%v'. non-alphanumerics are not supported", m.ConfigTable)
|
||||
}
|
||||
if len(tbl) > maxIdentifierLength {
|
||||
return m, fmt.Errorf(ErrorTooLongFieldLength+" - tableName : '%v'. max allowed field length is %v ", tbl, maxIdentifierLength)
|
||||
if len(m.ConfigTable) > maxIdentifierLength {
|
||||
return m, fmt.Errorf(ErrorTooLongFieldLength+" - tableName : '%v'. max allowed field length is %v ", m.ConfigTable, maxIdentifierLength)
|
||||
}
|
||||
m.configTable = tbl
|
||||
} else {
|
||||
return m, fmt.Errorf(ErrorMissingTableName)
|
||||
}
|
||||
// configure maxTimeout if provided
|
||||
if mxTimeout, ok := cmetadata.Properties[connMaxIdleTimeKey]; ok && mxTimeout != "" {
|
||||
if t, err := time.ParseDuration(mxTimeout); err == nil && t > 0 {
|
||||
m.maxIdleTimeout = t
|
||||
}
|
||||
}
|
||||
if m.maxIdleTimeout <= 0 {
|
||||
m.maxIdleTimeout = defaultMaxConnIdleTime
|
||||
if m.MaxIdleTimeout <= 0 {
|
||||
m.MaxIdleTimeout = defaultMaxConnIdleTime
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
@ -416,3 +416,11 @@ func (p *ConfigurationStore) subscribeToChannel(ctx context.Context, pgNotifyCha
|
|||
}
|
||||
return subscribeID, nil
|
||||
}
|
||||
|
||||
// GetComponentMetadata returns the metadata of the component.
|
||||
func (p *ConfigurationStore) GetComponentMetadata() map[string]string {
|
||||
metadataStruct := metadata{}
|
||||
metadataInfo := map[string]string{}
|
||||
contribMetadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo)
|
||||
return metadataInfo
|
||||
}
|
||||
|
|
|
@ -80,15 +80,15 @@ func TestPostgresbuildQuery(t *testing.T) {
|
|||
|
||||
func TestConnectAndQuery(t *testing.T) {
|
||||
m := metadata{
|
||||
connectionString: "mockConnectionString",
|
||||
configTable: "mockConfigTable",
|
||||
ConnectionString: "mockConnectionString",
|
||||
ConfigTable: "mockConfigTable",
|
||||
}
|
||||
|
||||
mock, err := pgxmock.NewPool()
|
||||
assert.Nil(t, err)
|
||||
defer mock.Close()
|
||||
|
||||
query := "SELECT EXISTS (SELECT FROM pg_tables where tablename = '" + m.configTable + "'"
|
||||
query := "SELECT EXISTS (SELECT FROM pg_tables where tablename = '" + m.ConfigTable + "'"
|
||||
mock.ExpectQuery(regexp.QuoteMeta(query)).
|
||||
WillReturnRows(pgxmock.NewRows(
|
||||
[]string{"exists"}).
|
||||
|
|
|
@ -16,12 +16,14 @@ package redis
|
|||
import "time"
|
||||
|
||||
type metadata struct {
|
||||
Host string
|
||||
Password string
|
||||
SentinelMasterName string
|
||||
MaxRetries int
|
||||
MaxRetryBackoff time.Duration
|
||||
EnableTLS bool
|
||||
Failover bool
|
||||
DB int
|
||||
Host string `mapstructure:"redishost"`
|
||||
Password string `mapstructure:"redisPassword"`
|
||||
SentinelMasterName string `mapstructure:"sentinelMasterName"`
|
||||
MaxRetries int `mapstructure:"maxRetries"`
|
||||
MaxRetryBackoff *int `mapstructure:"maxRetryBackoff"`
|
||||
EnableTLS bool `mapstructure:"enableTLS"`
|
||||
Failover bool `mapstructure:"failover"`
|
||||
DB int `mapstructure:"redisDB"`
|
||||
|
||||
internalMaxRetryBackoff time.Duration `mapstructure:"-"`
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
|
||||
"github.com/dapr/components-contrib/configuration"
|
||||
"github.com/dapr/components-contrib/configuration/redis/internal"
|
||||
contribMetadata "github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
|
@ -74,69 +76,29 @@ func NewRedisConfigurationStore(logger logger.Logger) configuration.Store {
|
|||
}
|
||||
|
||||
func parseRedisMetadata(meta configuration.Metadata) (metadata, error) {
|
||||
m := metadata{}
|
||||
m := metadata{
|
||||
EnableTLS: defaultEnableTLS,
|
||||
MaxRetries: defaultMaxRetries,
|
||||
internalMaxRetryBackoff: defaultMaxRetryBackoff,
|
||||
Failover: false,
|
||||
DB: defaultDB,
|
||||
}
|
||||
decodeErr := contribMetadata.DecodeMetadata(meta.Properties, &m)
|
||||
if decodeErr != nil {
|
||||
return m, decodeErr
|
||||
}
|
||||
|
||||
if val, ok := meta.Properties[host]; ok && val != "" {
|
||||
m.Host = val
|
||||
} else {
|
||||
if m.Host == "" {
|
||||
return m, errors.New("redis store error: missing host address")
|
||||
}
|
||||
|
||||
if val, ok := meta.Properties[password]; ok && val != "" {
|
||||
m.Password = val
|
||||
}
|
||||
|
||||
m.EnableTLS = defaultEnableTLS
|
||||
if val, ok := meta.Properties[enableTLS]; ok && val != "" {
|
||||
tls, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("redis store error: can't parse enableTLS field: %s", err)
|
||||
}
|
||||
m.EnableTLS = tls
|
||||
}
|
||||
|
||||
m.MaxRetries = defaultMaxRetries
|
||||
if val, ok := meta.Properties[maxRetries]; ok && val != "" {
|
||||
parsedVal, err := strconv.ParseInt(val, defaultBase, defaultBitSize)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("redis store error: can't parse maxRetries field: %s", err)
|
||||
}
|
||||
m.MaxRetries = int(parsedVal)
|
||||
}
|
||||
|
||||
m.MaxRetryBackoff = defaultMaxRetryBackoff
|
||||
if val, ok := meta.Properties[maxRetryBackoff]; ok && val != "" {
|
||||
parsedVal, err := strconv.ParseInt(val, defaultBase, defaultBitSize)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("redis store error: can't parse maxRetryBackoff field: %s", err)
|
||||
}
|
||||
m.MaxRetryBackoff = time.Duration(parsedVal)
|
||||
}
|
||||
|
||||
if val, ok := meta.Properties[failover]; ok && val != "" {
|
||||
failover, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("redis store error: can't parse failover field: %s", err)
|
||||
}
|
||||
m.Failover = failover
|
||||
}
|
||||
|
||||
// set the sentinelMasterName only with failover == true.
|
||||
if m.Failover {
|
||||
if val, ok := meta.Properties[sentinelMasterName]; ok && val != "" {
|
||||
m.SentinelMasterName = val
|
||||
} else {
|
||||
return m, errors.New("redis store error: missing sentinelMasterName")
|
||||
}
|
||||
if m.Failover && m.SentinelMasterName == "" {
|
||||
return m, errors.New("redis store error: missing sentinelMasterName")
|
||||
}
|
||||
|
||||
m.DB = defaultDB
|
||||
if val, ok := meta.Properties[redisDB]; ok && val != "" {
|
||||
parsedVal, err := strconv.ParseInt(val, defaultBase, defaultBitSize)
|
||||
if err != nil {
|
||||
return m, fmt.Errorf("redis store error: can't parse redisDB field from metadata: %w", err)
|
||||
}
|
||||
m.DB = int(parsedVal)
|
||||
if m.MaxRetryBackoff != nil {
|
||||
m.internalMaxRetryBackoff = time.Duration(*m.MaxRetryBackoff)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
|
@ -171,7 +133,7 @@ func (r *ConfigurationStore) newClient(m metadata) *redis.Client {
|
|||
Password: m.Password,
|
||||
DB: m.DB,
|
||||
MaxRetries: m.MaxRetries,
|
||||
MaxRetryBackoff: m.MaxRetryBackoff,
|
||||
MaxRetryBackoff: m.internalMaxRetryBackoff,
|
||||
}
|
||||
|
||||
// tell the linter to skip a check here.
|
||||
|
@ -191,7 +153,7 @@ func (r *ConfigurationStore) newFailoverClient(m metadata) *redis.Client {
|
|||
SentinelAddrs: []string{r.metadata.Host},
|
||||
DB: m.DB,
|
||||
MaxRetries: m.MaxRetries,
|
||||
MaxRetryBackoff: m.MaxRetryBackoff,
|
||||
MaxRetryBackoff: m.internalMaxRetryBackoff,
|
||||
}
|
||||
|
||||
/* #nosec */
|
||||
|
@ -369,3 +331,11 @@ func (r *ConfigurationStore) handleSubscribedChange(ctx context.Context, req *co
|
|||
r.logger.Errorf("fail to call handler to notify event for configuration update subscribe: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetComponentMetadata returns the metadata of the component.
|
||||
func (r *ConfigurationStore) GetComponentMetadata() map[string]string {
|
||||
metadataStruct := metadata{}
|
||||
metadataInfo := map[string]string{}
|
||||
contribMetadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo)
|
||||
return metadataInfo
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ package redis
|
|||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -252,27 +251,27 @@ func Test_parseRedisMetadata(t *testing.T) {
|
|||
testProperties[sentinelMasterName] = "tesSentinelMasterName"
|
||||
testProperties[redisDB] = "1"
|
||||
testMetadata := metadata{
|
||||
Host: "testHost",
|
||||
Password: "testPassword",
|
||||
EnableTLS: true,
|
||||
MaxRetries: 10,
|
||||
MaxRetryBackoff: time.Second,
|
||||
Failover: true,
|
||||
SentinelMasterName: "tesSentinelMasterName",
|
||||
DB: 1,
|
||||
Host: "testHost",
|
||||
Password: "testPassword",
|
||||
EnableTLS: true,
|
||||
MaxRetries: 10,
|
||||
internalMaxRetryBackoff: time.Second,
|
||||
Failover: true,
|
||||
SentinelMasterName: "tesSentinelMasterName",
|
||||
DB: 1,
|
||||
}
|
||||
|
||||
testDefaultProperties := make(map[string]string)
|
||||
testDefaultProperties[host] = "testHost"
|
||||
defaultMetadata := metadata{
|
||||
Host: "testHost",
|
||||
Password: "",
|
||||
EnableTLS: defaultEnableTLS,
|
||||
MaxRetries: defaultMaxRetries,
|
||||
MaxRetryBackoff: defaultMaxRetryBackoff,
|
||||
Failover: false,
|
||||
SentinelMasterName: "",
|
||||
DB: defaultDB,
|
||||
Host: "testHost",
|
||||
Password: "",
|
||||
EnableTLS: defaultEnableTLS,
|
||||
MaxRetries: defaultMaxRetries,
|
||||
internalMaxRetryBackoff: defaultMaxRetryBackoff,
|
||||
Failover: false,
|
||||
SentinelMasterName: "",
|
||||
DB: defaultDB,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
|
@ -305,9 +304,14 @@ func Test_parseRedisMetadata(t *testing.T) {
|
|||
t.Errorf("parseRedisMetadata() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("parseRedisMetadata() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
assert.Equal(t, tt.want.Host, got.Host)
|
||||
assert.Equal(t, tt.want.Password, got.Password)
|
||||
assert.Equal(t, tt.want.EnableTLS, got.EnableTLS)
|
||||
assert.Equal(t, tt.want.MaxRetries, got.MaxRetries)
|
||||
assert.Equal(t, tt.want.internalMaxRetryBackoff, got.internalMaxRetryBackoff)
|
||||
assert.Equal(t, tt.want.Failover, got.Failover)
|
||||
assert.Equal(t, tt.want.SentinelMasterName, got.SentinelMasterName)
|
||||
assert.Equal(t, tt.want.DB, got.DB)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,9 @@ type Store interface {
|
|||
|
||||
// Unsubscribe configuration with keys
|
||||
Unsubscribe(ctx context.Context, req *UnsubscribeRequest) error
|
||||
|
||||
// GetComponentMetadata returns information on the component's metadata.
|
||||
GetComponentMetadata() map[string]string
|
||||
}
|
||||
|
||||
// UpdateHandler is the handler used to send event to daprd.
|
||||
|
|
|
@ -16,12 +16,14 @@ package redis
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
rediscomponent "github.com/dapr/components-contrib/internal/component/redis"
|
||||
"github.com/dapr/components-contrib/lock"
|
||||
contribMetadata "github.com/dapr/components-contrib/metadata"
|
||||
"github.com/dapr/kit/logger"
|
||||
)
|
||||
|
||||
|
@ -186,3 +188,11 @@ func (r *StandaloneRedisLock) Close() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetComponentMetadata returns the metadata of the component.
|
||||
func (r *StandaloneRedisLock) GetComponentMetadata() map[string]string {
|
||||
metadataStruct := rediscomponent.Metadata{}
|
||||
metadataInfo := map[string]string{}
|
||||
contribMetadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo)
|
||||
return metadataInfo
|
||||
}
|
||||
|
|
|
@ -24,4 +24,7 @@ type Store interface {
|
|||
|
||||
// Unlock tries to release a lock.
|
||||
Unlock(ctx context.Context, req *UnlockRequest) (*UnlockResponse, error)
|
||||
|
||||
// GetComponentMetadata returns information on the component's metadata.
|
||||
GetComponentMetadata() map[string]string
|
||||
}
|
||||
|
|
|
@ -140,6 +140,14 @@ func GetMetadataProperty(props map[string]string, keys ...string) (val string, o
|
|||
// DecodeMetadata decodes metadata into a struct
|
||||
// This is an extension of mitchellh/mapstructure which also supports decoding durations
|
||||
func DecodeMetadata(input interface{}, result interface{}) error {
|
||||
// avoids a common mistake of passing the metadata object, instead of the properties map
|
||||
// if input is not of type map[string]string, then cast to metadata.Base and access the Properties
|
||||
if _, ok := input.(map[string]string); !ok {
|
||||
if base, ok := input.(Base); ok {
|
||||
input = base.Properties
|
||||
}
|
||||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
toTimeDurationHookFunc(),
|
||||
|
|
Loading…
Reference in New Issue