From 2266e7ad440a6c9110826d03be6f8ff4e25b064a Mon Sep 17 00:00:00 2001 From: Bernd Verst Date: Sat, 25 Mar 2023 15:34:48 -0700 Subject: [PATCH] Standardize metadata parsing in configstores and lockstore (#2710) Signed-off-by: Bernd Verst --- configuration/azure/appconfig/appconfig.go | 99 ++++++++----------- .../azure/appconfig/appconfig_test.go | 67 +++++++------ configuration/azure/appconfig/metadata.go | 19 ++-- configuration/postgres/metadata.go | 6 +- configuration/postgres/postgres.go | 58 ++++++----- configuration/postgres/postgres_test.go | 6 +- configuration/redis/metadata.go | 18 ++-- configuration/redis/redis.go | 86 ++++++---------- configuration/redis/redis_test.go | 44 +++++---- configuration/store.go | 3 + lock/redis/standalone.go | 10 ++ lock/store.go | 3 + metadata/utils.go | 8 ++ 13 files changed, 212 insertions(+), 215 deletions(-) diff --git a/configuration/azure/appconfig/appconfig.go b/configuration/azure/appconfig/appconfig.go index 41c539f31..7e2a33658 100644 --- a/configuration/azure/appconfig/appconfig.go +++ b/configuration/azure/appconfig/appconfig.go @@ -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 +} diff --git a/configuration/azure/appconfig/appconfig_test.go b/configuration/azure/appconfig/appconfig_test.go index 2d7e3b03d..371f0e1f7 100644 --- a/configuration/azure/appconfig/appconfig_test.go +++ b/configuration/azure/appconfig/appconfig_test.go @@ -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) { diff --git a/configuration/azure/appconfig/metadata.go b/configuration/azure/appconfig/metadata.go index 617e0e44b..75d7de35a 100644 --- a/configuration/azure/appconfig/metadata.go +++ b/configuration/azure/appconfig/metadata.go @@ -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:"-"` } diff --git a/configuration/postgres/metadata.go b/configuration/postgres/metadata.go index 456ce6570..0bf10cb74 100644 --- a/configuration/postgres/metadata.go +++ b/configuration/postgres/metadata.go @@ -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"` } diff --git a/configuration/postgres/postgres.go b/configuration/postgres/postgres.go index d894e1536..9a159b7df 100644 --- a/configuration/postgres/postgres.go +++ b/configuration/postgres/postgres.go @@ -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 +} diff --git a/configuration/postgres/postgres_test.go b/configuration/postgres/postgres_test.go index e6f9e4f33..4a9666582 100644 --- a/configuration/postgres/postgres_test.go +++ b/configuration/postgres/postgres_test.go @@ -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"}). diff --git a/configuration/redis/metadata.go b/configuration/redis/metadata.go index 5987da123..cd3aa1026 100644 --- a/configuration/redis/metadata.go +++ b/configuration/redis/metadata.go @@ -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:"-"` } diff --git a/configuration/redis/redis.go b/configuration/redis/redis.go index 7256163c6..da878b686 100644 --- a/configuration/redis/redis.go +++ b/configuration/redis/redis.go @@ -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 +} diff --git a/configuration/redis/redis_test.go b/configuration/redis/redis_test.go index 936af93c6..ef43ea2b4 100644 --- a/configuration/redis/redis_test.go +++ b/configuration/redis/redis_test.go @@ -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) }) } } diff --git a/configuration/store.go b/configuration/store.go index 84af8b6a8..0a82b4d8a 100644 --- a/configuration/store.go +++ b/configuration/store.go @@ -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. diff --git a/lock/redis/standalone.go b/lock/redis/standalone.go index 0f1753376..49453ceeb 100644 --- a/lock/redis/standalone.go +++ b/lock/redis/standalone.go @@ -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 +} diff --git a/lock/store.go b/lock/store.go index 5507f9f5e..9617a9d11 100644 --- a/lock/store.go +++ b/lock/store.go @@ -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 } diff --git a/metadata/utils.go b/metadata/utils.go index e9fede8d9..8877faffb 100644 --- a/metadata/utils.go +++ b/metadata/utils.go @@ -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(),