Skip to content

Commit

Permalink
Refactor config to be more structured (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
Evertras authored Nov 7, 2024
1 parent 9a83482 commit 914331c
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 106 deletions.
94 changes: 48 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,43 +111,7 @@ Instances that are listening will produce output when they receive messages.
Configuration is available through command line flags, a configuration file,
and/or environment variables.

#### Configuration File

A configuration file can be provided. This is useful when trying to template
configuration such as with Consul or similar tools.

A full configuration file with all options is given below.

```yaml
# my-cyn-config.yaml
listen-udp:
- 192.168.58.4:2345
listen-tcp:
- 192.168.58.4:2346
send-udp:
- 192.168.58.3:1234
send-tcp:
- 192.168.58.3:1235
send-interval: 30s
send-data: "my-custom-data"
http:
address: 127.0.0.1:8080
sinks:
stdout:
enabled: true
```
The configuration file must be supplied with the `--config/-c` command line
flag.

```bash
cyn --config ./my-cyn-config.yaml
# Can also use -c for shorthand
cyn -c ./my-cyn-config.yaml
```

### Command line flags
#### Command line flags

All available flags can be seen by running `cyn --help`. This README snippet should
have the latest, but when in doubt, just run the command to check.
Expand All @@ -163,30 +127,68 @@ Available Commands:
version Displays the version

Flags:
-c, --config-file string A file path to load as additional configuration.
-c, --config string A file path to load as additional configuration.
-h, --help help for this command
--http.address string An address:port to host an HTTP server on for realtime data, such as '127.0.0.1:8080'
-t, --listen-tcp strings An IP:port address to listen on for TCP. Can be specified multiple times.
-u, --listen-udp strings An IP:port address to listen on for UDP. Can be specified multiple times.
-d, --send-data string The string data to send. (default "hi")
-i, --send-interval duration How long to wait between attempting to send data (default 1s)
-T, --send-tcp strings An IP:port address to send to (TCP). Can be specified multiple times.
-U, --send-udp strings An IP:port address to send to (UDP). Can be specified multiple times.
-t, --listen.tcp strings An IP:port address to listen on for TCP. Can be specified multiple times.
-u, --listen.udp strings An IP:port address to listen on for UDP. Can be specified multiple times.
-d, --send.data string The string data to send. (default "hi")
-i, --send.interval duration How long to wait between attempting to send data (default 1s)
-T, --send.tcp strings An IP:port address to send to (TCP). Can be specified multiple times.
-U, --send.udp strings An IP:port address to send to (UDP). Can be specified multiple times.
--sinks.stdout.enabled Whether to enable the stdout metrics sink

Use " [command] --help" for more information about a command.
```

### Environment variables
#### Environment variables

Configuration can be supplied throuh environment variables that follow the
`CYNOMYS_VAR_NAME` pattern, replacing `-` with `_`. For example:
`CYNOMYS_VAR_NAME` pattern, replacing `.` with `_`. For example:

```bash
export CYNOMYS_SEND_INTERVAL=500ms
cyn -T 192.168.58.3:1234
```

#### Configuration File

A configuration file can be provided. This is useful when trying to template
configuration such as with Consul or similar tools.

A full configuration file with all options is given below.

```yaml
# my-cyn-config.yaml
listen:
udp:
- 192.168.58.4:2345
tcp:
- 192.168.58.4:2346
send:
udp:
- 192.168.58.3:1234
tcp:
- 192.168.58.3:1235
interval: 30s
data: "my custom data"
http:
address: 127.0.0.1:8080
sinks:
stdout:
enabled: true
```
The configuration file must be supplied with the `--config/-c` command line
flag.

```bash
cyn --config ./my-cyn-config.yaml
# Can also use -c for shorthand
cyn -c ./my-cyn-config.yaml
```

## Why the name

[Prarie dogs talk to each other](https://en.wikipedia.org/wiki/Prairie_dog)
Expand Down
51 changes: 29 additions & 22 deletions cmd/cyn/cmds/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@ import (
)

var config struct {
ListenUDP []string `mapstructure:"listen-udp"`
ListenTCP []string `mapstructure:"listen-tcp"`
SendUDP []string `mapstructure:"send-udp"`
SendTCP []string `mapstructure:"send-tcp"`
SendInterval time.Duration `mapstructure:"send-interval"`
SendData string `mapstructure:"send-data"`
HTTPServer struct {
Listen struct {
Udp []string `mapstructure:"udp"`
Tcp []string `mapstructure:"tcp"`
} `mapstructure:"listen"`

Send struct {
Udp []string `mapstructure:"udp"`
Tcp []string `mapstructure:"tcp"`

Interval time.Duration `mapstructure:"interval"`
Data string `mapstructure:"data"`
} `mapstructure:"send"`

HTTPServer struct {
Address string `mapstructure:"address"`
} `mapstructure:"http"`

Expand All @@ -44,14 +51,14 @@ func init() {
flags := rootCmd.Flags()

// Special flag for config
flags.StringVarP(&configFilePath, "config-file", "c", "", "A file path to load as additional configuration.")

flags.StringSliceP("listen-udp", "u", nil, "An IP:port address to listen on for UDP. Can be specified multiple times.")
flags.StringSliceP("listen-tcp", "t", nil, "An IP:port address to listen on for TCP. Can be specified multiple times.")
flags.StringSliceP("send-udp", "U", nil, "An IP:port address to send to (UDP). Can be specified multiple times.")
flags.StringSliceP("send-tcp", "T", nil, "An IP:port address to send to (TCP). Can be specified multiple times.")
flags.StringP("send-data", "d", "hi", "The string data to send.")
flags.DurationP("send-interval", "i", time.Second, "How long to wait between attempting to send data")
flags.StringVarP(&configFilePath, "config", "c", "", "A file path to load as additional configuration.")

flags.StringSliceP("listen.udp", "u", nil, "An IP:port address to listen on for UDP. Can be specified multiple times.")
flags.StringSliceP("listen.tcp", "t", nil, "An IP:port address to listen on for TCP. Can be specified multiple times.")
flags.StringSliceP("send.udp", "U", nil, "An IP:port address to send to (UDP). Can be specified multiple times.")
flags.StringSliceP("send.tcp", "T", nil, "An IP:port address to send to (TCP). Can be specified multiple times.")
flags.StringP("send.data", "d", "hi", "The string data to send.")
flags.DurationP("send.interval", "i", time.Second, "How long to wait between attempting to send data")
flags.String("http.address", "", "An address:port to host an HTTP server on for realtime data, such as '127.0.0.1:8080'")
flags.Bool("sinks.stdout.enabled", false, "Whether to enable the stdout metrics sink")

Expand All @@ -68,7 +75,7 @@ func initConfig() {
}

viper.SetEnvPrefix("CYNOMYS")
viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()

// Ignore errors here because we don't necessarily need a config file
Expand Down Expand Up @@ -99,7 +106,7 @@ var rootCmd = &cobra.Command{
instance.AddHTTPServer(config.HTTPServer.Address)
}

for _, listenOnUDP := range config.ListenUDP {
for _, listenOnUDP := range config.Listen.Udp {
addr, err := net.ResolveUDPAddr("udp", listenOnUDP)

if err != nil {
Expand All @@ -109,7 +116,7 @@ var rootCmd = &cobra.Command{
instance.AddUDPListener(listener.NewUDP(*addr))
}

for _, listenOnTCP := range config.ListenTCP {
for _, listenOnTCP := range config.Listen.Tcp {
addr, err := net.ResolveTCPAddr("tcp", listenOnTCP)

if err != nil {
Expand All @@ -119,26 +126,26 @@ var rootCmd = &cobra.Command{
instance.AddTCPListener(listener.NewTCP(*addr))
}

for _, sendUDPTo := range config.SendUDP {
for _, sendUDPTo := range config.Send.Udp {
addr, err := net.ResolveUDPAddr("udp", sendUDPTo)

if err != nil {
return fmt.Errorf("net.ResolveUDPAddr for %q: %w", sendUDPTo, err)
}

instance.AddUDPSender(sender.NewUDPSender(*addr, config.SendInterval, sink, []byte(config.SendData)))
instance.AddUDPSender(sender.NewUDPSender(*addr, config.Send.Interval, sink, []byte(config.Send.Data)))
}

// We could probably generalize this a bit better, but it's short enough
// not to care for now.
for _, sendTCPTo := range config.SendTCP {
for _, sendTCPTo := range config.Send.Tcp {
addr, err := net.ResolveTCPAddr("tcp", sendTCPTo)

if err != nil {
return fmt.Errorf("net.ResolveTCPAddr for %q: %w", sendTCPTo, err)
}

instance.AddTCPSender(sender.NewTCPSender(*addr, config.SendInterval, sink, []byte(config.SendData)))
instance.AddTCPSender(sender.NewTCPSender(*addr, config.Send.Interval, sink, []byte(config.Send.Data)))
}

return instance.Run()
Expand Down
2 changes: 1 addition & 1 deletion tests/features/http-server.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: host HTTP server for live status
So that I can visually check if data is flowing

Scenario: http server is enabled
Given I run cyn --listen-tcp 127.0.0.1:24184 --http.address 127.0.0.1:24185 --listen-udp 127.0.0.1:24183
Given I run cyn --listen.tcp 127.0.0.1:24184 --http.address 127.0.0.1:24185 --listen.udp 127.0.0.1:24183
And I wait a moment
# TODO: better verification, this is quick and dirty for now
Then the page at http://127.0.0.1:24185 contains "table"
Expand Down
13 changes: 7 additions & 6 deletions tests/features/listen-tcp.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ Feature: listen for TCP
Cyn should accept incoming TCP connections

Scenario: nothing is sent
Given I run cyn --listen-tcp 127.0.0.1:24563
Given I run cyn --listen.tcp 127.0.0.1:24563
When I wait 1 second
Then there is no output

Scenario: a TCP connection is made with no data sent
Given I run cyn --listen-tcp 127.0.0.1:24564
Given I run cyn --listen.tcp 127.0.0.1:24564
When I connect with TCP to 127.0.0.1:24564
And I wait a moment
Then the stdout contains "TCP connected"

Scenario: a TCP connection is made and then disconnected
Given I run cyn --listen-tcp 127.0.0.1:24565
Given I run cyn --listen.tcp 127.0.0.1:24565
When I connect with TCP to 127.0.0.1:24565
And I wait a moment
And I disconnect my TCP connection
Expand All @@ -30,11 +30,12 @@ Feature: listen for TCP
Then the stdout contains "TCP connected"
And the stdout contains "my tcp stuff"

Scenario: the listener is set via config file
Scenario: the tcp listener is set via config file
Given a configuration file that contains:
"""
listen-tcp:
- 127.0.0.1:14568
listen:
tcp:
- 127.0.0.1:14568
"""
And cyn is started with the config file
When I connect with TCP to 127.0.0.1:14568
Expand Down
11 changes: 6 additions & 5 deletions tests/features/listen-udp.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ Feature: listen for UDP

Scenario: nothing is sent
#Given cyn is listening for UDP on 127.0.0.1:14563
Given I run cyn --listen-udp 127.0.0.1:14563
Given I run cyn --listen.udp 127.0.0.1:14563
When I wait 1 second
Then there is no output

Scenario: a single UDP packet is sent
Given I run cyn --listen-udp 127.0.0.1:14564
Given I run cyn --listen.udp 127.0.0.1:14564
When I send a UDP packet containing "hello" to 127.0.0.1:14564
Then the stdout contains "hello"

Expand All @@ -20,11 +20,12 @@ Feature: listen for UDP
Then the stdout contains "hello"
And the stdout contains "another"

Scenario: the listener is set via config file
Scenario: the udp listener is set via config file
Given a configuration file that contains:
"""
listen-udp:
- 127.0.0.1:14568
listen:
udp:
- 127.0.0.1:14568
"""
And cyn is started with the config file
When I send a UDP packet containing "hello" to 127.0.0.1:14568
Expand Down
11 changes: 6 additions & 5 deletions tests/features/sink-stdout.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ Feature: watch latency in stdout

Scenario: no latency in stdout by default
Given I run cyn -u 127.0.0.1:14567
And I run cyn -U 127.0.0.1:14567 --send-interval 200ms
And I run cyn -U 127.0.0.1:14567 --send.interval 200ms
When I wait 1 second
Then the stdout does not contain "latency"

Scenario: latency is displayed in stdout when asked for
Given I run cyn -u 127.0.0.1:14567
And I run cyn -U 127.0.0.1:14567 --send-interval 200ms --sinks.stdout.enabled
And I run cyn -U 127.0.0.1:14567 --send.interval 200ms --sinks.stdout.enabled
When I wait 1 second
Then the stdout contains "latency"

Scenario: latency is displayed in stdout when configured via file
Given a configuration file that contains:
"""
send-udp:
- 127.0.0.1:14567
send-interval: 10ms
send:
interval: 10ms
udp:
- 127.0.0.1:14567
sinks:
stdout:
enabled: true
Expand Down
26 changes: 15 additions & 11 deletions tests/features/tcp.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Feature: send and receive TCP
Cyn should both send and receive on TCP connections

Scenario: one tries to connect to nothing
Given I run cyn --send-tcp 127.0.0.1:1234
Given I run cyn --send.tcp 127.0.0.1:1234
When I wait a moment
Then the stdout contains "connection refused"

Expand Down Expand Up @@ -34,14 +34,16 @@ Feature: send and receive TCP
And I wait 1 second
Then the stdout does not contain "broken pipe"

Scenario: an instance is set to call itself via config file
Scenario: a tcp instance is set to call itself via config file
Given a configuration file that contains:
"""
listen-tcp:
- 127.0.0.1:24568
send-tcp:
- 127.0.0.1:24568
send-interval: 10ms
listen:
tcp:
- 127.0.0.1:24568
send:
interval: 10ms
tcp:
- 127.0.0.1:24568
"""
When cyn is started with the config file
And I wait 1 second
Expand All @@ -50,10 +52,12 @@ Feature: send and receive TCP
Scenario: an instance is set to call itself via config file and the send interval is set via env variable
Given a configuration file that contains:
"""
listen-tcp:
- 127.0.0.1:24568
send-tcp:
- 127.0.0.1:24568
listen:
tcp:
- 127.0.0.1:24568
send:
tcp:
- 127.0.0.1:24568
"""
And the environment variable CYNOMYS_SEND_INTERVAL is set to "10ms"
When cyn is started with the config file
Expand Down
Loading

0 comments on commit 914331c

Please sign in to comment.