Standardize metadata parsing in configstores and lockstore (#2710)

Signed-off-by: Bernd Verst <github@bernd.dev>
This commit is contained in:
Bernd Verst 2023-03-25 15:34:48 -07:00 committed by GitHub
parent 3dc2691635
commit 2266e7ad44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 212 additions and 215 deletions

View File

@ -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
}

View File

@ -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) {

View File

@ -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:"-"`
}

View File

@ -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"`
}

View File

@ -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
}

View File

@ -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"}).

View File

@ -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:"-"`
}

View File

@ -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
}

View File

@ -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)
})
}
}

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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(),