mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-12 06:24:10 +01:00
cmd: change how default configuration values are built
For the orchestrator, we need to build default values for slice of configurations. We introduce a Reset() method that will be called by mapstructure.
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -77,6 +78,7 @@ func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config inte
|
||||
}
|
||||
|
||||
// Parse provided configuration
|
||||
defaultHook, disableDefaultHook := DefaultHook()
|
||||
var metadata mapstructure.Metadata
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
Result: &config,
|
||||
@@ -89,6 +91,7 @@ func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config inte
|
||||
return key == field
|
||||
},
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
defaultHook,
|
||||
flow.ConfigurationUnmarshalerHook(),
|
||||
clickhouse.NetworkNamesUnmarshalerHook(),
|
||||
mapstructure.TextUnmarshallerHookFunc(),
|
||||
@@ -102,6 +105,7 @@ func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config inte
|
||||
if err := decoder.Decode(rawConfig); err != nil {
|
||||
return fmt.Errorf("unable to parse configuration: %w", err)
|
||||
}
|
||||
disableDefaultHook()
|
||||
|
||||
// Override with environment variables
|
||||
for _, keyval := range os.Environ() {
|
||||
@@ -171,3 +175,42 @@ func (c ConfigRelatedOptions) Parse(out io.Writer, component string, config inte
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultHook will reset the destination value to its default using
|
||||
// the Reset() method if present.
|
||||
func DefaultHook() (mapstructure.DecodeHookFunc, func()) {
|
||||
disabled := false
|
||||
hook := func(from, to reflect.Value) (interface{}, error) {
|
||||
if disabled {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
if to.Kind() == reflect.Ptr {
|
||||
// We already have a pointer
|
||||
method, ok := to.Type().MethodByName("Reset")
|
||||
if !ok {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
if to.IsNil() {
|
||||
new := reflect.New(to.Type().Elem())
|
||||
method.Func.Call([]reflect.Value{new})
|
||||
to.Set(new)
|
||||
return from.Interface(), nil
|
||||
}
|
||||
method.Func.Call([]reflect.Value{to})
|
||||
return from.Interface(), nil
|
||||
}
|
||||
// Not a pointer, let's check if we take a pointer
|
||||
method, ok := reflect.PointerTo(to.Type()).MethodByName("Reset")
|
||||
if !ok {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
method.Func.Call([]reflect.Value{to.Addr()})
|
||||
|
||||
// Resume decoding
|
||||
return from.Interface(), nil
|
||||
}
|
||||
disable := func() {
|
||||
disabled = true
|
||||
}
|
||||
return hook, disable
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ type dummyModule2DetailsConfiguration struct {
|
||||
IntervalValue time.Duration
|
||||
}
|
||||
|
||||
func dummyDefaultConfiguration() dummyConfiguration {
|
||||
return dummyConfiguration{
|
||||
func (c *dummyConfiguration) Reset() {
|
||||
*c = dummyConfiguration{
|
||||
Module1: dummyModule1Configuration{
|
||||
Listen: "127.0.0.1:8080",
|
||||
Topic: "nothingness",
|
||||
@@ -79,7 +79,7 @@ module1:
|
||||
Path: configFile,
|
||||
}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err == nil {
|
||||
t.Fatal("Parse() didn't error")
|
||||
@@ -113,7 +113,7 @@ module2:
|
||||
Dump: true,
|
||||
}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
@@ -213,7 +213,7 @@ module2:
|
||||
Dump: true,
|
||||
}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
@@ -267,7 +267,7 @@ module2:
|
||||
Dump: true,
|
||||
}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
@@ -312,7 +312,7 @@ module1:
|
||||
|
||||
c := cmd.ConfigRelatedOptions{Path: configFile}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
@@ -332,7 +332,7 @@ module1:
|
||||
|
||||
c := cmd.ConfigRelatedOptions{Path: configFile}
|
||||
|
||||
parsed := dummyDefaultConfiguration()
|
||||
parsed := dummyConfiguration{}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if err := c.Parse(out, "dummy", &parsed); err == nil {
|
||||
t.Fatal("Parse() didn't error")
|
||||
@@ -343,3 +343,87 @@ invalid key "unused"`); diff != "" {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultInSlice(t *testing.T) {
|
||||
try := func(t *testing.T, parse func(cmd.ConfigRelatedOptions, *bytes.Buffer) interface{}) {
|
||||
// Configuration file
|
||||
config := `---
|
||||
modules:
|
||||
- module1:
|
||||
topic: flows1
|
||||
- module1:
|
||||
topic: flows2
|
||||
`
|
||||
configFile := filepath.Join(t.TempDir(), "config.yaml")
|
||||
ioutil.WriteFile(configFile, []byte(config), 0644)
|
||||
|
||||
c := cmd.ConfigRelatedOptions{
|
||||
Path: configFile,
|
||||
Dump: true,
|
||||
}
|
||||
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
parsed := parse(c, out)
|
||||
// Expected configuration
|
||||
expected := map[string][]dummyConfiguration{
|
||||
"Modules": {
|
||||
{
|
||||
Module1: dummyModule1Configuration{
|
||||
Listen: "127.0.0.1:8080",
|
||||
Topic: "flows1",
|
||||
Workers: 100,
|
||||
},
|
||||
Module2: dummyModule2Configuration{
|
||||
MoreDetails: MoreDetails{
|
||||
Stuff: "hello",
|
||||
},
|
||||
Details: dummyModule2DetailsConfiguration{
|
||||
Workers: 1,
|
||||
IntervalValue: time.Minute,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
Module1: dummyModule1Configuration{
|
||||
Listen: "127.0.0.1:8080",
|
||||
Topic: "flows2",
|
||||
Workers: 100,
|
||||
},
|
||||
Module2: dummyModule2Configuration{
|
||||
MoreDetails: MoreDetails{
|
||||
Stuff: "hello",
|
||||
},
|
||||
Details: dummyModule2DetailsConfiguration{
|
||||
Workers: 1,
|
||||
IntervalValue: time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
if diff := helpers.Diff(parsed, expected); diff != "" {
|
||||
t.Errorf("Parse() (-got, +want):\n%s", diff)
|
||||
}
|
||||
}
|
||||
t.Run("without pointer", func(t *testing.T) {
|
||||
try(t, func(c cmd.ConfigRelatedOptions, out *bytes.Buffer) interface{} {
|
||||
parsed := struct {
|
||||
Modules []dummyConfiguration
|
||||
}{}
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
}
|
||||
return parsed
|
||||
})
|
||||
})
|
||||
t.Run("with pointer", func(t *testing.T) {
|
||||
try(t, func(c cmd.ConfigRelatedOptions, out *bytes.Buffer) interface{} {
|
||||
parsed := struct {
|
||||
Modules []*dummyConfiguration
|
||||
}{}
|
||||
if err := c.Parse(out, "dummy", &parsed); err != nil {
|
||||
t.Fatalf("Parse() error:\n%+v", err)
|
||||
}
|
||||
return parsed
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ type ConsoleConfiguration struct {
|
||||
Database database.Configuration
|
||||
}
|
||||
|
||||
// DefaultConsoleConfiguration is the default configuration for the console command.
|
||||
func DefaultConsoleConfiguration() ConsoleConfiguration {
|
||||
return ConsoleConfiguration{
|
||||
// Reset resets the console configuration to its default value.
|
||||
func (c *ConsoleConfiguration) Reset() {
|
||||
*c = ConsoleConfiguration{
|
||||
HTTP: http.DefaultConfiguration(),
|
||||
Reporting: reporter.DefaultConfiguration(),
|
||||
Console: console.DefaultConfiguration(),
|
||||
@@ -55,7 +55,7 @@ var consoleCmd = &cobra.Command{
|
||||
manage collected flows.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := DefaultConsoleConfiguration()
|
||||
config := ConsoleConfiguration{}
|
||||
ConsoleOptions.Path = args[0]
|
||||
if err := ConsoleOptions.Parse(cmd.OutOrStdout(), "console", &config); err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
|
||||
func TestConsoleStart(t *testing.T) {
|
||||
r := reporter.NewMock(t)
|
||||
if err := consoleStart(r, DefaultConsoleConfiguration(), true); err != nil {
|
||||
config := ConsoleConfiguration{}
|
||||
config.Reset()
|
||||
if err := consoleStart(r, config, true); err != nil {
|
||||
t.Fatalf("consoleStart() error:\n%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ type InletConfiguration struct {
|
||||
Core core.Configuration
|
||||
}
|
||||
|
||||
// DefaultInletConfiguration is the default configuration for the inlet command.
|
||||
func DefaultInletConfiguration() InletConfiguration {
|
||||
return InletConfiguration{
|
||||
// Reset resets the configuration for the inlet command to its default value.
|
||||
func (c *InletConfiguration) Reset() {
|
||||
*c = InletConfiguration{
|
||||
HTTP: http.DefaultConfiguration(),
|
||||
Reporting: reporter.DefaultConfiguration(),
|
||||
Flow: flow.DefaultConfiguration(),
|
||||
@@ -58,7 +58,7 @@ var inletCmd = &cobra.Command{
|
||||
hydration and export to Kafka.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := DefaultInletConfiguration()
|
||||
config := InletConfiguration{}
|
||||
InletOptions.Path = args[0]
|
||||
if err := InletOptions.Parse(cmd.OutOrStdout(), "inlet", &config); err != nil {
|
||||
return err
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
|
||||
func TestInletStart(t *testing.T) {
|
||||
r := reporter.NewMock(t)
|
||||
if err := inletStart(r, DefaultInletConfiguration(), true); err != nil {
|
||||
config := InletConfiguration{}
|
||||
config.Reset()
|
||||
if err := inletStart(r, config, true); err != nil {
|
||||
t.Fatalf("inletStart() error:\n%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ type OrchestratorConfiguration struct {
|
||||
Console []ConsoleConfiguration
|
||||
}
|
||||
|
||||
// DefaultOrchestratorConfiguration is the default configuration for the orchestrator command.
|
||||
func DefaultOrchestratorConfiguration() OrchestratorConfiguration {
|
||||
return OrchestratorConfiguration{
|
||||
// Reset resets the configuration of the orchestrator command to its default value.
|
||||
func (c *OrchestratorConfiguration) Reset() {
|
||||
*c = OrchestratorConfiguration{
|
||||
Reporting: reporter.DefaultConfiguration(),
|
||||
HTTP: http.DefaultConfiguration(),
|
||||
ClickHouseDB: clickhousedb.DefaultConfiguration(),
|
||||
@@ -40,8 +40,8 @@ func DefaultOrchestratorConfiguration() OrchestratorConfiguration {
|
||||
Kafka: kafka.DefaultConfiguration(),
|
||||
Orchestrator: orchestrator.DefaultConfiguration(),
|
||||
// Other service configurations
|
||||
Inlet: []InletConfiguration{DefaultInletConfiguration()},
|
||||
Console: []ConsoleConfiguration{DefaultConsoleConfiguration()},
|
||||
Inlet: []InletConfiguration{},
|
||||
Console: []ConsoleConfiguration{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ var orchestratorCmd = &cobra.Command{
|
||||
components and centralizes configuration of the various other components.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config := DefaultOrchestratorConfiguration()
|
||||
config := OrchestratorConfiguration{}
|
||||
OrchestratorOptions.Path = args[0]
|
||||
OrchestratorOptions.BeforeDump = func() {
|
||||
// Override some parts of the configuration
|
||||
|
||||
@@ -11,7 +11,9 @@ import (
|
||||
|
||||
func TestOrchestratorStart(t *testing.T) {
|
||||
r := reporter.NewMock(t)
|
||||
if err := orchestratorStart(r, DefaultOrchestratorConfiguration(), true); err != nil {
|
||||
config := OrchestratorConfiguration{}
|
||||
config.Reset()
|
||||
if err := orchestratorStart(r, config, true); err != nil {
|
||||
t.Fatalf("orchestratorStart() error:\n%+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user