Skip to content

Commit 2811c07

Browse files
authored
feat(config)!: consolidate custom config parsing into config.Extended (#475)
* feat(config)!: consolidate custom config parsing into config.Extended Signed-off-by: Marc Nuri <marc@marcnuri.com> * review(config)!: consolidate custom config parsing into config.Extended Signed-off-by: Marc Nuri <marc@marcnuri.com> --------- Signed-off-by: Marc Nuri <marc@marcnuri.com>
1 parent dd32126 commit 2811c07

File tree

8 files changed

+238
-164
lines changed

8 files changed

+238
-164
lines changed

pkg/config/config.go

Lines changed: 10 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ type StaticConfig struct {
7373
ToolsetConfigs map[string]toml.Primitive `toml:"toolset_configs,omitempty"`
7474

7575
// Internal: parsed provider configs (not exposed to TOML package)
76-
parsedClusterProviderConfigs map[string]ProviderConfig
76+
parsedClusterProviderConfigs map[string]Extended
7777
// Internal: parsed toolset configs (not exposed to TOML package)
78-
parsedToolsetConfigs map[string]ToolsetConfig
78+
parsedToolsetConfigs map[string]Extended
7979

8080
// Internal: the config.toml directory, to help resolve relative file paths
8181
configDirPath string
@@ -129,87 +129,28 @@ func ReadToml(configData []byte, opts ...ReadConfigOpt) (*StaticConfig, error) {
129129
opt(config)
130130
}
131131

132-
if err := config.parseClusterProviderConfigs(md); err != nil {
132+
ctx := withConfigDirPath(context.Background(), config.configDirPath)
133+
134+
config.parsedClusterProviderConfigs, err = providerConfigRegistry.parse(ctx, md, config.ClusterProviderConfigs)
135+
if err != nil {
133136
return nil, err
134137
}
135138

136-
if err := config.parseToolsetConfigs(md); err != nil {
139+
config.parsedToolsetConfigs, err = toolsetConfigRegistry.parse(ctx, md, config.ToolsetConfigs)
140+
if err != nil {
137141
return nil, err
138142
}
139143

140144
return config, nil
141145
}
142146

143-
func (c *StaticConfig) GetProviderConfig(strategy string) (ProviderConfig, bool) {
147+
func (c *StaticConfig) GetProviderConfig(strategy string) (Extended, bool) {
144148
config, ok := c.parsedClusterProviderConfigs[strategy]
145149

146150
return config, ok
147151
}
148152

149-
func (c *StaticConfig) parseClusterProviderConfigs(md toml.MetaData) error {
150-
if c.parsedClusterProviderConfigs == nil {
151-
c.parsedClusterProviderConfigs = make(map[string]ProviderConfig, len(c.ClusterProviderConfigs))
152-
}
153-
154-
ctx := withConfigDirPath(context.Background(), c.configDirPath)
155-
156-
for strategy, primitive := range c.ClusterProviderConfigs {
157-
parser, ok := getProviderConfigParser(strategy)
158-
if !ok {
159-
continue
160-
}
161-
162-
providerConfig, err := parser(ctx, primitive, md)
163-
if err != nil {
164-
return fmt.Errorf("failed to parse config for ClusterProvider '%s': %w", strategy, err)
165-
}
166-
167-
if err := providerConfig.Validate(); err != nil {
168-
return fmt.Errorf("invalid config file for ClusterProvider '%s': %w", strategy, err)
169-
}
170-
171-
c.parsedClusterProviderConfigs[strategy] = providerConfig
172-
}
173-
174-
return nil
175-
}
176-
177-
func (c *StaticConfig) parseToolsetConfigs(md toml.MetaData) error {
178-
if c.parsedToolsetConfigs == nil {
179-
c.parsedToolsetConfigs = make(map[string]ToolsetConfig, len(c.ToolsetConfigs))
180-
}
181-
182-
ctx := withConfigDirPath(context.Background(), c.configDirPath)
183-
184-
for name, primitive := range c.ToolsetConfigs {
185-
parser, ok := getToolsetConfigParser(name)
186-
if !ok {
187-
continue
188-
}
189-
190-
toolsetConfig, err := parser(ctx, primitive, md)
191-
if err != nil {
192-
return fmt.Errorf("failed to parse config for Toolset '%s': %w", name, err)
193-
}
194-
195-
if err := toolsetConfig.Validate(); err != nil {
196-
return fmt.Errorf("invalid config file for Toolset '%s': %w", name, err)
197-
}
198-
199-
c.parsedToolsetConfigs[name] = toolsetConfig
200-
}
201-
202-
return nil
203-
}
204-
205-
func (c *StaticConfig) GetToolsetConfig(name string) (ToolsetConfig, bool) {
153+
func (c *StaticConfig) GetToolsetConfig(name string) (Extended, bool) {
206154
cfg, ok := c.parsedToolsetConfigs[name]
207155
return cfg, ok
208156
}
209-
210-
func (c *StaticConfig) SetToolsetConfig(name string, cfg ToolsetConfig) {
211-
if c.parsedToolsetConfigs == nil {
212-
c.parsedToolsetConfigs = make(map[string]ToolsetConfig)
213-
}
214-
c.parsedToolsetConfigs[name] = cfg
215-
}

pkg/config/context.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package config
2+
3+
import "context"
4+
5+
type configDirPathKey struct{}
6+
7+
func withConfigDirPath(ctx context.Context, dirPath string) context.Context {
8+
return context.WithValue(ctx, configDirPathKey{}, dirPath)
9+
}
10+
11+
func ConfigDirPathFromContext(ctx context.Context) string {
12+
val := ctx.Value(configDirPathKey{})
13+
14+
if val == nil {
15+
return ""
16+
}
17+
18+
if strVal, ok := val.(string); ok {
19+
return strVal
20+
}
21+
22+
return ""
23+
}

pkg/config/extended.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package config
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/BurntSushi/toml"
8+
)
9+
10+
// Extended is the interface that all configuration extensions must implement.
11+
// Each extended config manager registers a factory function to parse its config from TOML primitives
12+
type Extended interface {
13+
Validate() error
14+
}
15+
16+
type ExtendedConfigParser func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error)
17+
18+
type extendedConfigRegistry struct {
19+
parsers map[string]ExtendedConfigParser
20+
}
21+
22+
func newExtendedConfigRegistry() *extendedConfigRegistry {
23+
return &extendedConfigRegistry{
24+
parsers: make(map[string]ExtendedConfigParser),
25+
}
26+
}
27+
28+
func (r *extendedConfigRegistry) register(name string, parser ExtendedConfigParser) {
29+
if _, exists := r.parsers[name]; exists {
30+
panic("extended config parser already registered for name: " + name)
31+
}
32+
33+
r.parsers[name] = parser
34+
}
35+
36+
func (r *extendedConfigRegistry) parse(ctx context.Context, metaData toml.MetaData, configs map[string]toml.Primitive) (map[string]Extended, error) {
37+
if len(configs) == 0 {
38+
return make(map[string]Extended), nil
39+
}
40+
parsedConfigs := make(map[string]Extended, len(configs))
41+
42+
for name, primitive := range configs {
43+
parser, ok := r.parsers[name]
44+
if !ok {
45+
continue
46+
}
47+
48+
extendedConfig, err := parser(ctx, primitive, metaData)
49+
if err != nil {
50+
return nil, fmt.Errorf("failed to parse extended config for '%s': %w", name, err)
51+
}
52+
53+
if err = extendedConfig.Validate(); err != nil {
54+
return nil, fmt.Errorf("failed to validate extended config for '%s': %w", name, err)
55+
}
56+
57+
parsedConfigs[name] = extendedConfig
58+
}
59+
60+
return parsedConfigs, nil
61+
}

pkg/config/provider_config.go

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,7 @@
11
package config
22

3-
import (
4-
"context"
5-
"fmt"
3+
var providerConfigRegistry = newExtendedConfigRegistry()
64

7-
"github.com/BurntSushi/toml"
8-
)
9-
10-
// ProviderConfig is the interface that all provider-specific configurations must implement.
11-
// Each provider registers a factory function to parse its config from TOML primitives
12-
type ProviderConfig interface {
13-
Validate() error
14-
}
15-
16-
type ProviderConfigParser func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (ProviderConfig, error)
17-
18-
type configDirPathKey struct{}
19-
20-
func withConfigDirPath(ctx context.Context, dirPath string) context.Context {
21-
return context.WithValue(ctx, configDirPathKey{}, dirPath)
22-
}
23-
24-
func ConfigDirPathFromContext(ctx context.Context) string {
25-
val := ctx.Value(configDirPathKey{})
26-
27-
if val == nil {
28-
return ""
29-
}
30-
31-
if strVal, ok := val.(string); ok {
32-
return strVal
33-
}
34-
35-
return ""
36-
}
37-
38-
var (
39-
providerConfigParsers = make(map[string]ProviderConfigParser)
40-
)
41-
42-
func RegisterProviderConfig(strategy string, parser ProviderConfigParser) {
43-
if _, exists := providerConfigParsers[strategy]; exists {
44-
panic(fmt.Sprintf("provider config parser already registered for strategy '%s'", strategy))
45-
}
46-
47-
providerConfigParsers[strategy] = parser
48-
}
49-
50-
func getProviderConfigParser(strategy string) (ProviderConfigParser, bool) {
51-
provider, ok := providerConfigParsers[strategy]
52-
53-
return provider, ok
5+
func RegisterProviderConfig(name string, parser ExtendedConfigParser) {
6+
providerConfigRegistry.register(name, parser)
547
}

pkg/config/provider_config_test.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,16 @@ import (
1212

1313
type ProviderConfigSuite struct {
1414
BaseConfigSuite
15-
originalProviderConfigParsers map[string]ProviderConfigParser
15+
originalProviderConfigRegistry *extendedConfigRegistry
1616
}
1717

1818
func (s *ProviderConfigSuite) SetupTest() {
19-
s.originalProviderConfigParsers = make(map[string]ProviderConfigParser)
20-
for k, v := range providerConfigParsers {
21-
s.originalProviderConfigParsers[k] = v
22-
}
19+
s.originalProviderConfigRegistry = providerConfigRegistry
20+
providerConfigRegistry = newExtendedConfigRegistry()
2321
}
2422

2523
func (s *ProviderConfigSuite) TearDownTest() {
26-
providerConfigParsers = make(map[string]ProviderConfigParser)
27-
for k, v := range s.originalProviderConfigParsers {
28-
providerConfigParsers[k] = v
29-
}
24+
providerConfigRegistry = s.originalProviderConfigRegistry
3025
}
3126

3227
type ProviderConfigForTest struct {
@@ -35,7 +30,7 @@ type ProviderConfigForTest struct {
3530
IntProp int `toml:"int_prop"`
3631
}
3732

38-
var _ ProviderConfig = (*ProviderConfigForTest)(nil)
33+
var _ Extended = (*ProviderConfigForTest)(nil)
3934

4035
func (p *ProviderConfigForTest) Validate() error {
4136
if p.StrProp == "force-error" {
@@ -44,7 +39,7 @@ func (p *ProviderConfigForTest) Validate() error {
4439
return nil
4540
}
4641

47-
func providerConfigForTestParser(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (ProviderConfig, error) {
42+
func providerConfigForTestParser(_ context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) {
4843
var providerConfigForTest ProviderConfigForTest
4944
if err := md.PrimitiveDecode(primitive, &providerConfigForTest); err != nil {
5045
return nil, err
@@ -133,7 +128,7 @@ func (s *ProviderConfigSuite) TestReadConfigUnregisteredProviderConfig() {
133128
}
134129

135130
func (s *ProviderConfigSuite) TestReadConfigParserError() {
136-
RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (ProviderConfig, error) {
131+
RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) {
137132
return nil, errors.New("parser error forced by test")
138133
})
139134
invalidConfigPath := s.writeConfig(`
@@ -156,7 +151,7 @@ func (s *ProviderConfigSuite) TestReadConfigParserError() {
156151

157152
func (s *ProviderConfigSuite) TestConfigDirPathInContext() {
158153
var capturedDirPath string
159-
RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (ProviderConfig, error) {
154+
RegisterProviderConfig("test", func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (Extended, error) {
160155
capturedDirPath = ConfigDirPathFromContext(ctx)
161156
var providerConfigForTest ProviderConfigForTest
162157
if err := md.PrimitiveDecode(primitive, &providerConfigForTest); err != nil {

pkg/config/toolset_config.go

Lines changed: 3 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,7 @@
11
package config
22

3-
import (
4-
"context"
5-
"fmt"
3+
var toolsetConfigRegistry = newExtendedConfigRegistry()
64

7-
"github.com/BurntSushi/toml"
8-
)
9-
10-
// ToolsetConfig is the interface that all toolset-specific configurations must implement.
11-
// Each toolset registers a factory function to parse its config from TOML primitives
12-
type ToolsetConfig interface {
13-
Validate() error
14-
}
15-
16-
type ToolsetConfigParser func(ctx context.Context, primitive toml.Primitive, md toml.MetaData) (ToolsetConfig, error)
17-
18-
var (
19-
toolsetConfigParsers = make(map[string]ToolsetConfigParser)
20-
)
21-
22-
func RegisterToolsetConfig(name string, parser ToolsetConfigParser) {
23-
if _, exists := toolsetConfigParsers[name]; exists {
24-
panic(fmt.Sprintf("toolset config parser already registered for toolset '%s'", name))
25-
}
26-
27-
toolsetConfigParsers[name] = parser
28-
}
29-
30-
func getToolsetConfigParser(name string) (ToolsetConfigParser, bool) {
31-
parser, ok := toolsetConfigParsers[name]
32-
33-
return parser, ok
5+
func RegisterToolsetConfig(name string, parser ExtendedConfigParser) {
6+
toolsetConfigRegistry.register(name, parser)
347
}

0 commit comments

Comments
 (0)