diff --git a/cmd/chirpstack-gateway-bridge/cmd/configfile.go b/cmd/chirpstack-gateway-bridge/cmd/configfile.go index 47e73b9d..84c7368a 100644 --- a/cmd/chirpstack-gateway-bridge/cmd/configfile.go +++ b/cmd/chirpstack-gateway-bridge/cmd/configfile.go @@ -418,6 +418,15 @@ marshaler="{{ .Integration.Marshaler }}" # Max. execution duration. max_execution_duration="{{ .MetaData.Dynamic.MaxExecutionDuration }}" + # Split delimiter. + # + # When the output of a command returns multiple lines, ChirpStack Gateway Bridge + # assumes multiple values are returned. In this case it will split by the given delimiter + # to obtain the key / value of each row. The key will be prefixed with the name of the + # configured command. + split_delimiter="{{ .MetaData.Dynamic.SplitDelimiter }}" + + # Commands to execute. # # The value of the stdout will be used as the key value (string). diff --git a/cmd/chirpstack-gateway-bridge/cmd/root.go b/cmd/chirpstack-gateway-bridge/cmd/root.go index 31fc920b..acd4e798 100644 --- a/cmd/chirpstack-gateway-bridge/cmd/root.go +++ b/cmd/chirpstack-gateway-bridge/cmd/root.go @@ -67,6 +67,7 @@ func init() { viper.SetDefault("integration.mqtt.auth.azure_iot_hub.sas_token_expiration", 24*time.Hour) + viper.SetDefault("meta_data.dynamic.split_delimiter", "=") viper.SetDefault("meta_data.dynamic.execution_interval", time.Minute) viper.SetDefault("meta_data.dynamic.max_execution_duration", time.Second) diff --git a/docs/content/install/config.md b/docs/content/install/config.md index 4adc7ff2..cf9f6df6 100644 --- a/docs/content/install/config.md +++ b/docs/content/install/config.md @@ -446,6 +446,15 @@ marshaler="protobuf" # Max. execution duration. max_execution_duration="1s" + # Split delimiter. + # + # When the output of a command returns multiple lines, ChirpStack Gateway Bridge + # assumes multiple values are returned. In this case it will split by the given delimiter + # to obtain the key / value of each row. The key will be prefixed with the name of the + # configured command. + split_delimiter="=" + + # Commands to execute. # # The value of the stdout will be used as the key value (string). diff --git a/internal/config/config.go b/internal/config/config.go index 61c71e36..353f2f71 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -110,13 +110,14 @@ type Config struct { EndpointEnabled bool `mapstructure:"endpoint_enabled"` Bind string `mapstructure:"bind"` } `mapstructure:"prometheus"` - } `mapstructure:"metrics"` + } `mapstructure:"metrics"` MetaData struct { Static map[string]string `mapstructure:"static"` Dynamic struct { ExecutionInterval time.Duration `mapstructure:"execution_interval"` MaxExecutionDuration time.Duration `mapstructure:"max_execution_duration"` + SplitDelimiter string `mapstructure:"split_delimiter"` Commands map[string]string `mapstructure:"commands"` } `mapstructure:"dynamic"` } `mapstructure:"meta_data"` diff --git a/internal/metadata/metadata.go b/internal/metadata/metadata.go index cd9a2488..c495c0c8 100644 --- a/internal/metadata/metadata.go +++ b/internal/metadata/metadata.go @@ -22,8 +22,9 @@ var ( cmnds map[string]string cached map[string]string - interval time.Duration - maxExecution time.Duration + interval time.Duration + maxExecution time.Duration + splitDelimiter string ) // Setup configures the metadata package. @@ -36,6 +37,7 @@ func Setup(conf config.Config) error { interval = conf.MetaData.Dynamic.ExecutionInterval maxExecution = conf.MetaData.Dynamic.MaxExecutionDuration + splitDelimiter = conf.MetaData.Dynamic.SplitDelimiter go func() { for { @@ -71,7 +73,23 @@ func runCommands() { continue } - newKV[k] = out + if strings.Contains(out, "\n") { + rows := strings.Split(out, "\n") + for _, row := range rows { + kv := strings.Split(row, splitDelimiter) + if len(kv) != 2 { + log.WithFields(log.Fields{ + "row": row, + "split_delimiter": splitDelimiter, + }).Warning("metadata: can not split output in key / value") + } else { + newKV[k+"_"+kv[0]] = kv[1] + } + } + + } else { + newKV[k] = out + } } mux.Lock() diff --git a/internal/metadata/metadata_test.go b/internal/metadata/metadata_test.go index d6504790..b0a30699 100644 --- a/internal/metadata/metadata_test.go +++ b/internal/metadata/metadata_test.go @@ -116,9 +116,20 @@ func TestMetaData(t *testing.T) { "bar": "test2", }, }, + { + Name: "command returns multiple rows", + Commands: map[string]string{ + "bar": `echo -e "foo=bar\nalice=bob"`, + }, + Expected: map[string]string{ + "bar_foo": "bar", + "bar_alice": "bob", + }, + }, } maxExecution = time.Second + splitDelimiter = "=" for _, tst := range tests { t.Run(tst.Name, func(t *testing.T) {