Skip to content

Commit

Permalink
Merge pull request #20 from ethpandaops/feat/support-env-overrides
Browse files Browse the repository at this point in the history
feat(main): add support for env cfg override
  • Loading branch information
mattevans authored Jan 22, 2025
2 parents c30dd3f + e3e5e81 commit 4ea188f
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 16 deletions.
82 changes: 66 additions & 16 deletions cmd/sentry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,55 +496,105 @@ func (s *contributoor) initBeaconNode() error {

// applyConfigOverridesFromFlags applies CLI flags to the config if they are set.
func applyConfigOverridesFromFlags(cfg *config.Config, c *cli.Context) error {
// Apply CLI flags to config if they are set.
if c.String("network") != "" {
log.Infof("Overriding network to %s", c.String("network"))
// Apply environment variables first, then override with CLI flags if set
if network := os.Getenv("CONTRIBUTOOR_NETWORK"); network != "" {
log.Infof("Setting network from env to %s", network)
cfg.SetNetwork(network)
}

if c.String("network") != "" {
log.Infof("Overriding network from CLI to %s", c.String("network"))
cfg.SetNetwork(c.String("network"))
}

if c.String("beacon-node-address") != "" {
log.Infof("Overriding beacon node address")
if addr := os.Getenv("CONTRIBUTOOR_BEACON_NODE_ADDRESS"); addr != "" {
log.Infof("Setting beacon node address from env")
cfg.SetBeaconNodeAddress(addr)
}

if c.String("beacon-node-address") != "" {
log.Infof("Overriding beacon node address from CLI")
cfg.SetBeaconNodeAddress(c.String("beacon-node-address"))
}

if c.String("metrics-address") != "" {
log.Infof("Overriding metrics address to %s", c.String("metrics-address"))
if addr := os.Getenv("CONTRIBUTOOR_METRICS_ADDRESS"); addr != "" {
log.Infof("Setting metrics address from env to %s", addr)
cfg.SetMetricsAddress(addr)
}

if c.String("metrics-address") != "" {
log.Infof("Overriding metrics address from CLI to %s", c.String("metrics-address"))
cfg.SetMetricsAddress(c.String("metrics-address"))
}

if c.String("health-check-address") != "" {
log.Infof("Overriding health check address to %s", c.String("health-check-address"))
if addr := os.Getenv("CONTRIBUTOOR_HEALTH_CHECK_ADDRESS"); addr != "" {
log.Infof("Setting health check address from env to %s", addr)
cfg.SetHealthCheckAddress(addr)
}

if c.String("health-check-address") != "" {
log.Infof("Overriding health check address from CLI to %s", c.String("health-check-address"))
cfg.SetHealthCheckAddress(c.String("health-check-address"))
}

if c.String("log-level") != "" {
log.Infof("Overriding log level to %s", c.String("log-level"))
if level := os.Getenv("CONTRIBUTOOR_LOG_LEVEL"); level != "" {
log.Infof("Setting log level from env to %s", level)
cfg.SetLogLevel(level)
}

if c.String("log-level") != "" {
log.Infof("Overriding log level from CLI to %s", c.String("log-level"))
cfg.SetLogLevel(c.String("log-level"))
}

if c.String("output-server-address") != "" {
log.Infof("Overriding output server address")
if addr := os.Getenv("CONTRIBUTOOR_OUTPUT_SERVER_ADDRESS"); addr != "" {
log.Infof("Setting output server address from env")
cfg.SetOutputServerAddress(addr)
}

if c.String("output-server-address") != "" {
log.Infof("Overriding output server address from CLI")
cfg.SetOutputServerAddress(c.String("output-server-address"))
}

if c.String("username") != "" || c.String("password") != "" {
log.Infof("Overriding output server credentials")
// Handle credentials from env
var (
username = os.Getenv("CONTRIBUTOOR_USERNAME")
password = os.Getenv("CONTRIBUTOOR_PASSWORD")
)

if username != "" || password != "" {
log.Infof("Setting output server credentials from env")
cfg.SetOutputServerCredentials(
base64.StdEncoding.EncodeToString(
[]byte(fmt.Sprintf("%s:%s", username, password)),
),
)
}

// CLI flags override env vars for credentials
if c.String("username") != "" || c.String("password") != "" {
log.Infof("Overriding output server credentials from CLI")
cfg.SetOutputServerCredentials(
base64.StdEncoding.EncodeToString(
[]byte(fmt.Sprintf("%s:%s", c.String("username"), c.String("password"))),
),
)
}

if tls := os.Getenv("CONTRIBUTOOR_OUTPUT_SERVER_TLS"); tls != "" {
log.Infof("Setting output server tls from env to %s", tls)

tlsBool, err := strconv.ParseBool(tls)
if err != nil {
return errors.Wrap(err, "failed to parse output server tls env var")
}

cfg.SetOutputServerTLS(tlsBool)
}

if c.String("output-server-tls") != "" {
log.Infof("Overriding output server tls to %s", c.String("output-server-tls"))
log.Infof("Overriding output server tls from CLI to %s", c.String("output-server-tls"))

tls, err := strconv.ParseBool(c.String("output-server-tls"))
if err != nil {
Expand Down
171 changes: 171 additions & 0 deletions cmd/sentry/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"net"
"net/http"
"os"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -293,3 +295,172 @@ func TestApplyConfigOverridesFromFlags(t *testing.T) {
})
}
}

func TestConfigOverridePrecedence(t *testing.T) {
tests := []struct {
name string
configValue string
envValue string
cliValue string
expectedValue string
envVar string
cliFlag string
setter func(*config.Config, string)
getter func(*config.Config) string
}{
{
name: "CLI overrides env and config - network",
configValue: "mainnet",
envValue: "sepolia",
cliValue: "holesky",
expectedValue: "holesky",
envVar: "CONTRIBUTOOR_NETWORK",
cliFlag: "network",
setter: func(c *config.Config, v string) { c.SetNetwork(v) },
getter: func(c *config.Config) string { return strings.ToLower(c.NetworkName.DisplayName()) },
},
{
name: "Env overrides config but not CLI - beacon node",
configValue: "http://localhost:5052",
envValue: "http://beacon:5052",
cliValue: "",
expectedValue: "http://beacon:5052",
envVar: "CONTRIBUTOOR_BEACON_NODE_ADDRESS",
cliFlag: "beacon-node-address",
setter: func(c *config.Config, v string) { c.SetBeaconNodeAddress(v) },
getter: func(c *config.Config) string { return c.BeaconNodeAddress },
},
{
name: "Config value preserved when no overrides",
configValue: ":9090",
envValue: "",
cliValue: "",
expectedValue: ":9090",
envVar: "CONTRIBUTOOR_METRICS_ADDRESS",
cliFlag: "metrics-address",
setter: func(c *config.Config, v string) { c.SetMetricsAddress(v) },
getter: func(c *config.Config) string { return c.MetricsAddress },
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup initial config
cfg := config.NewDefaultConfig()
tt.setter(cfg, tt.configValue)

// Set env var if provided
if tt.envValue != "" {
os.Setenv(tt.envVar, tt.envValue)
defer os.Unsetenv(tt.envVar)
}

// Create CLI app with all flags
app := cli.NewApp()
app.Flags = []cli.Flag{
&cli.StringFlag{Name: "network"},
&cli.StringFlag{Name: "beacon-node-address"},
&cli.StringFlag{Name: "metrics-address"},
&cli.StringFlag{Name: "health-check-address"},
&cli.StringFlag{Name: "log-level"},
&cli.StringFlag{Name: "output-server-address"},
&cli.StringFlag{Name: "username"},
&cli.StringFlag{Name: "password"},
&cli.StringFlag{Name: "output-server-tls"},
}

// Set up action to apply config
app.Action = func(c *cli.Context) error {
return applyConfigOverridesFromFlags(cfg, c)
}

// Build args
args := []string{"app"}
if tt.cliValue != "" {
args = append(args, fmt.Sprintf("--%s", tt.cliFlag), tt.cliValue)
}

// Run app with args
err := app.Run(args)
require.NoError(t, err)

// Verify final value
assert.Equal(t, tt.expectedValue, tt.getter(cfg))
})
}
}

func TestCredentialsPrecedence(t *testing.T) {
tests := []struct {
name string
envUser string
envPass string
cliUser string
cliPass string
expectedCreds string
}{
{
name: "CLI credentials override env",
envUser: "env_user",
envPass: "env_pass",
cliUser: "cli_user",
cliPass: "cli_pass",
expectedCreds: "cli_user:cli_pass",
},
{
name: "Env credentials used when no CLI",
envUser: "env_user",
envPass: "env_pass",
cliUser: "",
cliPass: "",
expectedCreds: "env_user:env_pass",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := config.NewDefaultConfig()

// Set env vars if provided
if tt.envUser != "" {
os.Setenv("CONTRIBUTOOR_USERNAME", tt.envUser)
defer os.Unsetenv("CONTRIBUTOOR_USERNAME")
}
if tt.envPass != "" {
os.Setenv("CONTRIBUTOOR_PASSWORD", tt.envPass)
defer os.Unsetenv("CONTRIBUTOOR_PASSWORD")
}

// Create CLI app with all flags
app := cli.NewApp()
app.Flags = []cli.Flag{
&cli.StringFlag{Name: "username"},
&cli.StringFlag{Name: "password"},
}

// Set up action to apply config
app.Action = func(c *cli.Context) error {
return applyConfigOverridesFromFlags(cfg, c)
}

// Build args
args := []string{"app"}
if tt.cliUser != "" {
args = append(args, "--username", tt.cliUser)
}
if tt.cliPass != "" {
args = append(args, "--password", tt.cliPass)
}

// Run app with args
err := app.Run(args)
require.NoError(t, err)

// Decode and verify credentials
require.NotNil(t, cfg.OutputServer)
decoded, err := base64.StdEncoding.DecodeString(cfg.OutputServer.Credentials)
require.NoError(t, err)
assert.Equal(t, tt.expectedCreds, string(decoded))
})
}
}

0 comments on commit 4ea188f

Please sign in to comment.