Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/blitz/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func run(cmd *cobra.Command, args []string) error {
return err
}

if err := config.ValidateExclusiveGeneratorConfig(viper.GetViper()); err != nil {
return err
}

cfg := config.NewConfig()
if err := viper.Unmarshal(cfg); err != nil {
return fmt.Errorf("failed to unmarshal config: %w", err)
Expand Down
24 changes: 20 additions & 4 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ func (c *Config) Validate() error {
if err := c.Logging.Validate(); err != nil {
return err
}
if err := c.Generator.Validate(); err != nil {
if len(c.Generators) > 0 {
// Validate the defaulted entries, since that is what runs.
for i, g := range c.EffectiveGenerators() {
if err := g.Validate(); err != nil {
return fmt.Errorf("generators[%d] validation failed: %w", i, err)
}
}
} else if err := c.Generator.Validate(); err != nil {
return err
}
if len(c.Outputs) > 0 {
Expand Down Expand Up @@ -70,12 +77,21 @@ func (c *Config) Validate() error {

// EffectiveGenerators returns the list of generators to use.
// If Generators is set, it takes precedence over the singular Generator field.
// The singular Generator carries the defaults applied by the override system,
// so each list entry inherits any field it leaves unset (workers, and so on)
// from it, while user-set fields win.
// Comma-separated HostMetrics OS values are expanded into separate generators.
func (c *Config) EffectiveGenerators() []Generator {
if len(c.Generators) > 0 {
return expandGenerators(c.Generators)
if len(c.Generators) == 0 {
return expandGenerators([]Generator{c.Generator})
}
merged := make([]Generator, len(c.Generators))
for i, g := range c.Generators {
m := g
fillZeroFields(reflect.ValueOf(&m).Elem(), reflect.ValueOf(c.Generator))
merged[i] = m
Comment on lines +88 to +92

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has the same zero-fill problem, now for generators:. An explicit wel.manageEventSources: false becomes true, so blitz registers and deregisters event sources (which needs admin) after the user opted out. filegen.cache-enabled: false also becomes true.

}
return expandGenerators([]Generator{c.Generator})
return expandGenerators(merged)
}

// EffectiveOutputs returns the list of outputs to use.
Expand Down
52 changes: 52 additions & 0 deletions internal/config/generator_multi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package config

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestEffectiveGenerators_ListEntriesInheritDefaults(t *testing.T) {
// c.Generator is the viper-defaulted template (workers filled). A list
// entry that omits workers must inherit that default; user-set fields win.
cfg := &Config{
Generator: Generator{JSON: JSONGeneratorConfig{Workers: 1}},
Generators: []Generator{
{Type: GeneratorTypeJSON, JSON: JSONGeneratorConfig{Rate: time.Second}},
},
}

gens := cfg.EffectiveGenerators()
require.Len(t, gens, 1)
assert.Equal(t, 1, gens[0].JSON.Workers, "unset workers should inherit the template default")
assert.Equal(t, time.Second, gens[0].JSON.Rate, "user-set rate must be preserved")
}

func TestEffectiveGenerators_ListEntryUserValueOverridesDefault(t *testing.T) {
cfg := &Config{
Generator: Generator{JSON: JSONGeneratorConfig{Workers: 1}},
Generators: []Generator{
{Type: GeneratorTypeJSON, JSON: JSONGeneratorConfig{Workers: 8, Rate: time.Second}},
},
}

gens := cfg.EffectiveGenerators()
assert.Equal(t, 8, gens[0].JSON.Workers, "explicit workers must win over the template default")
}

func TestValidateExclusiveGeneratorConfig_BothSetFails(t *testing.T) {
v := viperFrom(t, "generator:\n type: json\ngenerators:\n - type: json\n")
require.Error(t, ValidateExclusiveGeneratorConfig(v))
}

func TestValidateExclusiveGeneratorConfig_OnlyGeneratorsOK(t *testing.T) {
v := viperFrom(t, "generators:\n - type: json\n")
require.NoError(t, ValidateExclusiveGeneratorConfig(v))
}

func TestValidateExclusiveGeneratorConfig_OnlyGeneratorOK(t *testing.T) {
v := viperFrom(t, "generator:\n type: json\n")
require.NoError(t, ValidateExclusiveGeneratorConfig(v))
}
7 changes: 7 additions & 0 deletions internal/config/output_exclusive.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ func ValidateExclusiveOutputConfig(v *viper.Viper) error {
return rejectBothForms(v, "output", "outputs")
}

// ValidateExclusiveGeneratorConfig rejects a config that sets both the
// singular `generator` and the plural `generators`, the same ambiguity the
// output check guards against.
func ValidateExclusiveGeneratorConfig(v *viper.Viper) error {
return rejectBothForms(v, "generator", "generators")
}

// rejectBothForms errors when both the singular and plural forms of a config
// key are present in the parsed config. Shared by the output check here and,
// later, the generator/generators check.
Expand Down
Loading