mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-06 13:09:52 +02:00
config: create helper for dumping config
With this commit we add a new helper function that recursively turns the runtime configuration into a flat key/value map that is human-readable, using the dot notation for nested values that is also used in the config file or command line flags.
This commit is contained in:
parent
2b54774721
commit
becbe7085d
93
config.go
93
config.go
@ -2111,3 +2111,96 @@ func checkEstimateMode(estimateMode string) error {
|
|||||||
return fmt.Errorf("estimatemode must be one of the following: %v",
|
return fmt.Errorf("estimatemode must be one of the following: %v",
|
||||||
bitcoindEstimateModes[:])
|
bitcoindEstimateModes[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// configToFlatMap converts the given config struct into a flat map of key/value
|
||||||
|
// pairs using the dot notation we are used to from the config file or command
|
||||||
|
// line flags.
|
||||||
|
func configToFlatMap(cfg Config) (map[string]string, error) {
|
||||||
|
result := make(map[string]string)
|
||||||
|
|
||||||
|
// redact is the helper function that redacts sensitive values like
|
||||||
|
// passwords.
|
||||||
|
redact := func(key, value string) string {
|
||||||
|
sensitiveKeySuffixes := []string{
|
||||||
|
"pass",
|
||||||
|
"password",
|
||||||
|
"dsn",
|
||||||
|
}
|
||||||
|
for _, suffix := range sensitiveKeySuffixes {
|
||||||
|
if strings.HasSuffix(key, suffix) {
|
||||||
|
return "[redacted]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// printConfig is the helper function that goes into nested structs
|
||||||
|
// recursively. Because we call it recursively, we need to declare it
|
||||||
|
// before we define it.
|
||||||
|
var printConfig func(reflect.Value, string)
|
||||||
|
printConfig = func(obj reflect.Value, prefix string) {
|
||||||
|
// Turn struct pointers into the actual struct, so we can
|
||||||
|
// iterate over the fields as we would with a struct value.
|
||||||
|
if obj.Kind() == reflect.Ptr {
|
||||||
|
obj = obj.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Abort on nil values.
|
||||||
|
if !obj.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop over all fields of the struct and inspect the type.
|
||||||
|
for i := 0; i < obj.NumField(); i++ {
|
||||||
|
field := obj.Field(i)
|
||||||
|
fieldType := obj.Type().Field(i)
|
||||||
|
|
||||||
|
longName := fieldType.Tag.Get("long")
|
||||||
|
namespace := fieldType.Tag.Get("namespace")
|
||||||
|
group := fieldType.Tag.Get("group")
|
||||||
|
switch {
|
||||||
|
// We have a long name defined, this is a config value.
|
||||||
|
case longName != "":
|
||||||
|
key := longName
|
||||||
|
if prefix != "" {
|
||||||
|
key = prefix + "." + key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the value directly to the flattened map.
|
||||||
|
result[key] = redact(key, fmt.Sprintf(
|
||||||
|
"%v", field.Interface(),
|
||||||
|
))
|
||||||
|
|
||||||
|
// We have no long name but a namespace, this is a
|
||||||
|
// nested struct.
|
||||||
|
case longName == "" && namespace != "":
|
||||||
|
key := namespace
|
||||||
|
if prefix != "" {
|
||||||
|
key = prefix + "." + key
|
||||||
|
}
|
||||||
|
|
||||||
|
printConfig(field, key)
|
||||||
|
|
||||||
|
// Just a group means this is a dummy struct to house
|
||||||
|
// multiple config values, the group name doesn't go
|
||||||
|
// into the final field name.
|
||||||
|
case longName == "" && group != "":
|
||||||
|
printConfig(field, prefix)
|
||||||
|
|
||||||
|
// Anonymous means embedded struct. We need to recurse
|
||||||
|
// into it but without adding anything to the prefix.
|
||||||
|
case fieldType.Anonymous:
|
||||||
|
printConfig(field, prefix)
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn the whole config struct into a flat map.
|
||||||
|
printConfig(reflect.ValueOf(cfg), "")
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
48
config_test.go
Normal file
48
config_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package lnd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testPassword = "testpassword"
|
||||||
|
redactedPassword = "[redacted]"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestConfigToFlatMap tests that the configToFlatMap function works as
|
||||||
|
// expected on the default configuration.
|
||||||
|
func TestConfigToFlatMap(t *testing.T) {
|
||||||
|
cfg := DefaultConfig()
|
||||||
|
cfg.BitcoindMode.RPCPass = testPassword
|
||||||
|
cfg.BtcdMode.RPCPass = testPassword
|
||||||
|
cfg.Tor.Password = testPassword
|
||||||
|
cfg.DB.Etcd.Pass = testPassword
|
||||||
|
cfg.DB.Postgres.Dsn = testPassword
|
||||||
|
|
||||||
|
result, err := configToFlatMap(cfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Pick a couple of random values to check.
|
||||||
|
require.Equal(t, DefaultLndDir, result["lnddir"])
|
||||||
|
require.Equal(
|
||||||
|
t, fmt.Sprintf("%v", chainreg.DefaultBitcoinTimeLockDelta),
|
||||||
|
result["bitcoin.timelockdelta"],
|
||||||
|
)
|
||||||
|
require.Equal(
|
||||||
|
t, fmt.Sprintf("%v", routing.DefaultAprioriWeight),
|
||||||
|
result["routerrpc.apriori.weight"],
|
||||||
|
)
|
||||||
|
require.Contains(t, result, "routerrpc.routermacaroonpath")
|
||||||
|
|
||||||
|
// Check that sensitive values are not included.
|
||||||
|
require.Equal(t, redactedPassword, result["bitcoind.rpcpass"])
|
||||||
|
require.Equal(t, redactedPassword, result["btcd.rpcpass"])
|
||||||
|
require.Equal(t, redactedPassword, result["tor.password"])
|
||||||
|
require.Equal(t, redactedPassword, result["db.etcd.pass"])
|
||||||
|
require.Equal(t, redactedPassword, result["db.postgres.dsn"])
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user