Skip to content

Commit

Permalink
Merge pull request #140 from launchdarkly/eb/ch82940/configtypes-lib
Browse files Browse the repository at this point in the history
(v6 - #8) use configtypes library for config validation
  • Loading branch information
eli-darkly authored Aug 4, 2020
2 parents 2546a6f + 75af80d commit a487574
Show file tree
Hide file tree
Showing 30 changed files with 485 additions and 722 deletions.
14 changes: 8 additions & 6 deletions cmd/ld-relay/ld-relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func main() {
flag.BoolVar(&useEnvironment, "from-env", false, "read configuration from environment variables")
flag.Parse()

c := config.DefaultConfig
var c config.Config
loggers := logging.MakeDefaultLoggers()

if configFile == "" && !useEnvironment {
Expand Down Expand Up @@ -87,23 +87,25 @@ func main() {
errs := make(chan error)
defer close(errs)

startHTTPServer(&c, r, loggers, errs)
port := c.Main.Port.GetOrElse(config.DefaultPort)

startHTTPServer(&c, port, r, loggers, errs)

for err := range errs {
loggers.Errorf("Error starting http listener on port: %d %s", c.Main.Port, err)
loggers.Errorf("Error starting http listener on port: %d %s", port, err)
os.Exit(1)
}
}

func startHTTPServer(c *config.Config, r *relay.Relay, loggers ldlog.Loggers, errs chan<- error) {
func startHTTPServer(c *config.Config, port int, r *relay.Relay, loggers ldlog.Loggers, errs chan<- error) {
srv := &http.Server{
Addr: fmt.Sprintf(":%d", c.Main.Port),
Addr: fmt.Sprintf(":%d", port),
Handler: r,
}

go func() {
var err error
loggers.Infof("Starting server listening on port %d\n", c.Main.Port)
loggers.Infof("Starting server listening on port %d\n", port)
if c.Main.TLSEnabled {
loggers.Infof("TLS Enabled for server")
err = srv.ListenAndServeTLS(c.Main.TLSCert, c.Main.TLSKey)
Expand Down
217 changes: 126 additions & 91 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package config
import (
"time"

ct "github.com/launchdarkly/go-configtypes"
"github.com/launchdarkly/ld-relay/v6/internal/logging"
)

const (
// DefaultPort is the port that Relay runs on if not otherwise specified.
DefaultPort = 8030

// DefaultBaseURI is the default value for the base URI of LaunchDarkly services (polling endpoints).
DefaultBaseURI = "https://app.launchdarkly.com"

Expand All @@ -16,6 +20,9 @@ const (
// DefaultEventsURI is the default value for the base URI of LaunchDarkly services (event endpoints).
DefaultEventsURI = "https://events.launchdarkly.com"

// DefaultEventCapacity is the default value for EventsConfig.Capacity if not specified.
DefaultEventCapacity = 1000

// DefaultHeartbeatInterval is the default value for MainConfig.HeartBeatInterval if not specified.
DefaultHeartbeatInterval = time.Minute * 3

Expand All @@ -24,19 +31,19 @@ const (

// DefaultDatabaseCacheTTL is the default value for the LocalTTL parameter for databases if not specified.
DefaultDatabaseCacheTTL = time.Second * 30

// DefaultPrometheusPort is the default value for PrometheusConfig.Port if not specified.
DefaultPrometheusPort = 8031
)

const (
defaultPort = 8030
defaultEventCapacity = 1000
defaultRedisHost = "localhost"
defaultRedisPort = 6379
defaultConsulHost = "localhost"
defaultPrometheusPort = 8031
defaultRedisHost = "localhost"
defaultRedisPort = 6379
defaultConsulHost = "localhost"
)

var (
defaultRedisURL = newOptAbsoluteURLMustBeValid("redis://localhost:6379")
defaultRedisURL, _ = ct.NewOptURLAbsoluteFromString("redis://localhost:6379")
)

// DefaultLoggers is the default logging configuration used by Relay.
Expand All @@ -46,8 +53,17 @@ var DefaultLoggers = logging.MakeDefaultLoggers()

// Config describes the configuration for a relay instance.
//
// If you are incorporating Relay into your own code and configuring it programmatically, it is best to
// start by copying relay.DefaultConfig and then changing only the fields you need to change.
// Some fields use special types that enforce validation rules, such as URL fields which must
// be absolute URLs, port numbers which must be greater than zero, or durations. This validation
// is done automatically when reading the configuration from a file or from environment variables.
//
// If you are incorporating Relay into your own code and configuring it programmatically, you
// may need to use functions from go-configtypes such as NewOptDuration to set fields that have
// validation rules.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type Config struct {
Main MainConfig
Events EventsConfig
Expand All @@ -56,98 +72,129 @@ type Config struct {
DynamoDB DynamoDBConfig
Environment map[string]*EnvConfig
Proxy ProxyConfig

// Optional configuration for metrics integrations. Note that unlike the other fields in Config,
// MetricsConfig is not the name of a configuration file section; the actual sections are the
// structs within this struct (Datadog, etc.).
MetricsConfig
}

// MainConfig contains global configuration options for Relay.
//
// This corresponds to the [Main] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type MainConfig struct {
ExitOnError bool
ExitAlways bool
IgnoreConnectionErrors bool
StreamURI OptAbsoluteURL
BaseURI OptAbsoluteURL
Port int
HeartbeatInterval OptDuration
MaxClientConnectionTime OptDuration
TLSEnabled bool
TLSCert string
TLSKey string
LogLevel OptLogLevel
ExitOnError bool `conf:"EXIT_ON_ERROR"`
ExitAlways bool `conf:"EXIT_ALWAYS"`
IgnoreConnectionErrors bool `conf:"IGNORE_CONNECTION_ERRORS"`
StreamURI ct.OptURLAbsolute `conf:"STREAM_URI"`
BaseURI ct.OptURLAbsolute `conf:"BASE_URI"`
Port ct.OptIntGreaterThanZero `conf:"PORT"`
HeartbeatInterval ct.OptDuration `conf:"HEARTBEAT_INTERVAL"`
MaxClientConnectionTime ct.OptDuration `conf:"MAX_CLIENT_CONNECTION_TIME"`
TLSEnabled bool `conf:"TLS_ENABLED"`
TLSCert string `conf:"TLS_CERT"`
TLSKey string `conf:"TLS_KEY"`
LogLevel OptLogLevel `conf:"LOG_LEVEL"`
}

// EventsConfig contains configuration parameters for proxying events.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type EventsConfig struct {
EventsURI OptAbsoluteURL
SendEvents bool
FlushInterval OptDuration
Capacity int
InlineUsers bool
EventsURI ct.OptURLAbsolute `conf:"EVENTS_HOST"`
SendEvents bool `conf:"USE_EVENTS"`
FlushInterval ct.OptDuration `conf:"EVENTS_FLUSH_INTERVAL"`
Capacity ct.OptIntGreaterThanZero `conf:"EVENTS_CAPACITY"`
InlineUsers bool `conf:"EVENTS_INLINE_USERS"`
}

// RedisConfig configures the optional Redis integration.
//
// Redis is enabled if URL or Host is non-empty or if Port is non-zero. If only Host or Port is set,
// Redis is enabled if URL or Host is non-empty or if Port is set. If only Host or Port is set,
// the other value is set to defaultRedisPort or defaultRedisHost. It is an error to set Host or
// Port if URL is also set.
//
// This corresponds to the [Redis] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type RedisConfig struct {
Host string
Port int
URL OptAbsoluteURL
LocalTTL OptDuration
TLS bool
Password string
Host string `conf:"REDIS_HOST"`
Port ct.OptIntGreaterThanZero
URL ct.OptURLAbsolute `conf:"REDIS_URL"`
LocalTTL ct.OptDuration `conf:"CACHE_TTL"`
TLS bool `conf:"REDIS_TLS"`
Password string `conf:"REDIS_PASSWORD"`
}

// ConsulConfig configures the optional Consul integration.
//
// Consul is enabled if Host is non-empty.
//
// This corresponds to the [Consul] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type ConsulConfig struct {
Host string
LocalTTL OptDuration
Host string `conf:"CONSUL_HOST"`
LocalTTL ct.OptDuration `conf:"CACHE_TTL"`
}

// DynamoDBConfig configures the optional DynamoDB integration, which is used only if Enabled is true.
//
// This corresponds to the [DynamoDB] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type DynamoDBConfig struct {
Enabled bool
TableName string
URL OptAbsoluteURL
LocalTTL OptDuration
Enabled bool `conf:"USE_DYNAMODB"`
TableName string `conf:"DYNAMODB_TABLE"`
URL ct.OptURLAbsolute `conf:"DYNAMODB_URL"`
LocalTTL ct.OptDuration `conf:"CACHE_TTL"`
}

// EnvConfig describes an environment to be relayed. There may be any number of these.
//
// This corresponds to one of the [environment "env-name"] sections in the configuration file. In the
// Config.Environment map, each key is an environment name and each value is an EnvConfig.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type EnvConfig struct {
SDKKey SDKKey
APIKey string // deprecated, equivalent to SdkKey
MobileKey MobileKey
EnvID EnvironmentID
Prefix string // used only if Redis, Consul, or DynamoDB is enabled
TableName string // used only if DynamoDB is enabled
AllowedOrigin []string
SecureMode bool
InsecureSkipVerify bool
LogLevel OptLogLevel
TTL OptDuration
SDKKey SDKKey // set from env var LD_ENV_envname
MobileKey MobileKey `conf:"LD_MOBILE_KEY_"`
EnvID EnvironmentID `conf:"LD_CLIENT_SIDE_ID_"`
Prefix string `conf:"LD_PREFIX_"` // used only if Redis, Consul, or DynamoDB is enabled
TableName string `conf:"LD_TABLE_NAME_"` // used only if DynamoDB is enabled
AllowedOrigin ct.OptStringList `conf:"LD_ALLOWED_ORIGIN_"`
SecureMode bool `conf:"LD_SECURE_MODE_"`
InsecureSkipVerify bool // deliberately not settable by env vars
LogLevel OptLogLevel `conf:"LD_LOG_LEVEL_"`
TTL ct.OptDuration `conf:"LD_TTL_"`
}

// ProxyConfig represents all the supported proxy options.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type ProxyConfig struct {
URL OptAbsoluteURL
NTLMAuth bool
User string
Password string
Domain string
CACertFiles string
URL ct.OptURLAbsolute `conf:"PROXY_URL"`
NTLMAuth bool `conf:"PROXY_AUTH_NTLM"`
User string `conf:"PROXY_AUTH_USER"`
Password string `conf:"PROXY_AUTH_PASSWORD"`
Domain string `conf:"PROXY_AUTH_DOMAIN"`
CACertFiles ct.OptStringList `conf:"PROXY_CA_CERTS"`
}

// MetricsConfig contains configurations for optional metrics integrations.
Expand All @@ -159,55 +206,43 @@ type MetricsConfig struct {
Prometheus PrometheusConfig
}

// CommonMetricsConfig contains fields that are common to DatadogCOnfig, StackdriverConfig, and PrometheusConfig.
type CommonMetricsConfig struct {
Enabled bool
Prefix string
}

// DatadogConfig configures the optional Datadog integration, which is used only if Enabled is true.
//
// This corresponds to the [Datadog] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type DatadogConfig struct {
TraceAddr string
StatsAddr string
Tag []string
CommonMetricsConfig
Enabled bool `conf:"USE_DATADOG"`
Prefix string `conf:"DATADOG_PREFIX"`
TraceAddr string `conf:"DATADOG_TRACE_ADDR"`
StatsAddr string `conf:"DATADOG_STATS_ADDR"`
Tag []string // special handling in LoadConfigFromEnvironment
}

// StackdriverConfig configures the optional Stackdriver integration, which is used only if Enabled is true.
//
// This corresponds to the [StackdriverConfig] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type StackdriverConfig struct {
ProjectID string
CommonMetricsConfig
Enabled bool `conf:"USE_STACKDRIVER"`
Prefix string `conf:"STACKDRIVER_PREFIX"`
ProjectID string `conf:"STACKDRIVER_PROJECT_ID"`
}

// PrometheusConfig configures the optional Prometheus integration, which is used only if Enabled is true.
//
// This corresponds to the [PrometheusConfig] section in the configuration file.
//
// Since configuration options can be set either programmatically, or from a file, or from environment
// variables, individual fields are not documented here; instead, see the `README.md` section on
// configuration.
type PrometheusConfig struct {
Port int
CommonMetricsConfig
}

// DefaultConfig contains defaults for all relay configuration sections.
//
// If you are incorporating Relay into your own code and configuring it programmatically, it is best to
// start by copying relay.DefaultConfig and then changing only the fields you need to change.
var DefaultConfig = Config{
Main: MainConfig{
BaseURI: newOptAbsoluteURLMustBeValid(DefaultBaseURI),
StreamURI: newOptAbsoluteURLMustBeValid(DefaultStreamURI),
Port: defaultPort,
},
Events: EventsConfig{
Capacity: defaultEventCapacity,
EventsURI: newOptAbsoluteURLMustBeValid(DefaultEventsURI),
},
MetricsConfig: MetricsConfig{
Prometheus: PrometheusConfig{
Port: defaultPrometheusPort,
},
},
Enabled bool `conf:"USE_PROMETHEUS"`
Prefix string `conf:"PROMETHEUS_PREFIX"`
Port ct.OptIntGreaterThanZero `conf:"PROMETHEUS_PORT"`
}
Loading

0 comments on commit a487574

Please sign in to comment.