Cleaner CLI options definition (env keys as tags)
This commit is contained in:
@@ -2,16 +2,18 @@ package options
|
||||
|
||||
type (
|
||||
DBOpt struct {
|
||||
DSN string
|
||||
Profiler string
|
||||
DSN string `env:"DB_DSN"`
|
||||
Profiler string `env:"DB_PROFILER"`
|
||||
}
|
||||
)
|
||||
|
||||
func DB(pfix string) (o *DBOpt) {
|
||||
o = &DBOpt{
|
||||
DSN: EnvString(pfix, "DB_DSN", "corteza:corteza@tcp(db:3306)/corteza?collation=utf8mb4_general_ci"),
|
||||
Profiler: EnvString(pfix, "DB_PROFILER", "none"),
|
||||
DSN: "corteza:corteza@tcp(db:3306)/corteza?collation=utf8mb4_general_ci",
|
||||
Profiler: "none",
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,12 +2,60 @@ package options
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func fill(opt interface{}, pfix string) {
|
||||
v := reflect.ValueOf(opt)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("expecting a pointer, not a value")
|
||||
}
|
||||
|
||||
if v.IsNil() {
|
||||
panic("nil pointer passed")
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
|
||||
length := v.NumField()
|
||||
for i := 0; i < length; i++ {
|
||||
f := v.Field(i)
|
||||
t := v.Type().Field(i)
|
||||
if tag := t.Tag.Get("env"); tag != "" {
|
||||
if !f.CanSet() {
|
||||
panic("unexpected pointer for field " + t.Name)
|
||||
}
|
||||
|
||||
if f.Type() == reflect.TypeOf(time.Duration(1)) {
|
||||
v.FieldByName(t.Name).SetInt(int64(EnvDuration(pfix, tag, time.Duration(f.Int()))))
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.String {
|
||||
v.FieldByName(t.Name).SetString(EnvString(pfix, tag, f.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.Bool {
|
||||
v.FieldByName(t.Name).SetBool(EnvBool(pfix, tag, f.Bool()))
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.Int {
|
||||
v.FieldByName(t.Name).SetInt(int64(EnvInt(pfix, tag, int(f.Int()))))
|
||||
continue
|
||||
}
|
||||
|
||||
panic("unsupported type/kind for field " + t.Name)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeEnvKeys(pfix, name string) []string {
|
||||
return []string{
|
||||
strings.ToUpper(strings.Trim(pfix, "_") + "_" + name),
|
||||
|
||||
+20
-18
@@ -6,34 +6,36 @@ import (
|
||||
|
||||
type (
|
||||
HTTPOpt struct {
|
||||
Addr string
|
||||
Logging bool
|
||||
Tracing bool
|
||||
Addr string `env:"HTTP_ADDR"`
|
||||
Logging bool `env:"HTTP_LOG_REQUESTS"`
|
||||
Tracing bool `env:"HTTP_ERROR_TRACING"`
|
||||
|
||||
EnableVersionRoute bool
|
||||
EnableDebugRoute bool
|
||||
EnableVersionRoute bool `env:"HTTP_ENABLE_VERSION_ROUTE"`
|
||||
EnableDebugRoute bool `env:"HTTP_ENABLE_DEBUG_ROUTE"`
|
||||
|
||||
EnableMetrics bool
|
||||
MetricsServiceLabel string
|
||||
MetricsUsername string
|
||||
MetricsPassword string
|
||||
EnableMetrics bool `env:"HTTP_METRICS"`
|
||||
MetricsServiceLabel string `env:"HTTP_METRICS_NAME"`
|
||||
MetricsUsername string `env:"HTTP_METRICS_USERNAME"`
|
||||
MetricsPassword string `env:"HTTP_METRICS_PASSWORD"`
|
||||
}
|
||||
)
|
||||
|
||||
func HTTP(pfix string) (o *HTTPOpt) {
|
||||
o = &HTTPOpt{
|
||||
Addr: EnvString(pfix, "HTTP_ADDR", ":80"),
|
||||
Logging: EnvBool(pfix, "HTTP_LOG_REQUESTS", true),
|
||||
Tracing: EnvBool(pfix, "HTTP_ERROR_TRACING", false),
|
||||
EnableVersionRoute: EnvBool(pfix, "HTTP_ENABLE_VERSION_ROUTE", true),
|
||||
EnableDebugRoute: EnvBool(pfix, "HTTP_ENABLE_DEBUG_ROUTE", false),
|
||||
EnableMetrics: EnvBool(pfix, "HTTP_METRICS", false),
|
||||
MetricsServiceLabel: EnvString(pfix, "HTTP_METRICS_NAME", "corteza"),
|
||||
MetricsUsername: EnvString(pfix, "HTTP_METRICS_USERNAME", "metrics"),
|
||||
Addr: ":80",
|
||||
Logging: true,
|
||||
Tracing: false,
|
||||
EnableVersionRoute: true,
|
||||
EnableDebugRoute: false,
|
||||
EnableMetrics: false,
|
||||
MetricsServiceLabel: "corteza",
|
||||
MetricsUsername: "metrics",
|
||||
|
||||
// Setting metrics password to random string to prevent security accidents...
|
||||
MetricsPassword: EnvString(pfix, "HTTP_METRICS_PASSWORD", string(rand.Bytes(5))),
|
||||
MetricsPassword: string(rand.Bytes(5)),
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,16 +6,18 @@ import (
|
||||
|
||||
type (
|
||||
HttpClientOpt struct {
|
||||
ClientTSLInsecure bool
|
||||
HttpClientTimeout time.Duration
|
||||
ClientTSLInsecure bool `env:"HTTP_CLIENT_TSL_INSECURE"`
|
||||
HttpClientTimeout time.Duration `env:"HTTP_CLIENT_TIMEOUT"`
|
||||
}
|
||||
)
|
||||
|
||||
func HttpClient(pfix string) (o *HttpClientOpt) {
|
||||
o = &HttpClientOpt{
|
||||
ClientTSLInsecure: EnvBool(pfix, "HTTP_CLIENT_TSL_INSECURE", false),
|
||||
HttpClientTimeout: EnvDuration(pfix, "HTTP_CLIENT_TIMEOUT", 30*time.Second),
|
||||
ClientTSLInsecure: false,
|
||||
HttpClientTimeout: 30 * time.Second,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,17 +8,20 @@ import (
|
||||
|
||||
type (
|
||||
JWTOpt struct {
|
||||
Secret string
|
||||
Expiry time.Duration
|
||||
Secret string `env:"AUTH_JWT_SECRET"`
|
||||
Expiry time.Duration `env:"AUTH_JWT_EXPIRY"`
|
||||
}
|
||||
)
|
||||
|
||||
func JWT(pfix string) (o *JWTOpt) {
|
||||
o = &JWTOpt{
|
||||
Secret: EnvString(pfix, "AUTH_JWT_SECRET", string(rand.Bytes(32))),
|
||||
// Setting JWT secret to random string to prevent security accidents...
|
||||
Expiry: EnvDuration(pfix, "AUTH_JWT_EXPIRY", time.Hour*24*30),
|
||||
Secret: string(rand.Bytes(32)),
|
||||
|
||||
Expiry: time.Hour * 24 * 30,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ type (
|
||||
// Logger's output leve is configured here, but
|
||||
// dev/prod configuration happens earlier
|
||||
LogOpt struct {
|
||||
Level string
|
||||
Level string `env:"LOG_LEVEL"`
|
||||
}
|
||||
)
|
||||
|
||||
func Log(pfix string) (o *LogOpt) {
|
||||
o = &LogOpt{
|
||||
Level: EnvString(pfix, "LOG_LEVEL", "info"),
|
||||
Level: "info",
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,14 +6,15 @@ import (
|
||||
|
||||
type (
|
||||
MonitorOpt struct {
|
||||
Interval time.Duration
|
||||
Interval time.Duration `env:"MONITOR_INTERVAL"`
|
||||
}
|
||||
)
|
||||
|
||||
func Monitor(pfix string) (o *MonitorOpt) {
|
||||
o = &MonitorOpt{
|
||||
Interval: EnvDuration(pfix, "MONITOR_INTERVAL", 300*time.Second),
|
||||
Interval: 300 * time.Second,
|
||||
}
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@ package options
|
||||
|
||||
type (
|
||||
ProvisionOpt struct {
|
||||
MigrateDatabase bool
|
||||
|
||||
AutoSetup bool
|
||||
MigrateDatabase bool `env:"PROVISION_MIGRATE_DATABASE"`
|
||||
AutoSetup bool `env:"PROVISION_AUTO_SETUP"`
|
||||
}
|
||||
)
|
||||
|
||||
func Provision(pfix string) (o *ProvisionOpt) {
|
||||
o = &ProvisionOpt{
|
||||
MigrateDatabase: EnvBool(pfix, "PROVISION_MIGRATE_DATABASE", true),
|
||||
AutoSetup: EnvBool(pfix, "PROVISION_AUTO_SETUP", true),
|
||||
MigrateDatabase: true,
|
||||
AutoSetup: true,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+14
-12
@@ -6,16 +6,16 @@ import (
|
||||
|
||||
type (
|
||||
PubSubOpt struct {
|
||||
Mode string
|
||||
Mode string `env:"PUBSUB_MODE"`
|
||||
|
||||
// Mode
|
||||
PollingInterval time.Duration
|
||||
PollingInterval time.Duration `env:"PUBSUB_POLLING_INTERVAL"`
|
||||
|
||||
// Redis
|
||||
RedisAddr string
|
||||
RedisTimeout time.Duration
|
||||
RedisPingTimeout time.Duration
|
||||
RedisPingPeriod time.Duration
|
||||
RedisAddr string `env:"PUBSUB_REDIS_ADDR"`
|
||||
RedisTimeout time.Duration `env:"PUBSUB_REDIS_TIMEOUT"`
|
||||
RedisPingTimeout time.Duration `env:"PUBSUB_REDIS_PING_TIMEOUT"`
|
||||
RedisPingPeriod time.Duration `env:"PUBSUB_REDIS_PING_PERIOD"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,13 +27,15 @@ func PubSub(pfix string) (o *PubSubOpt) {
|
||||
)
|
||||
|
||||
o = &PubSubOpt{
|
||||
Mode: EnvString(pfix, "PUBSUB_MODE", "poll"),
|
||||
PollingInterval: EnvDuration(pfix, "PUBSUB_POLLING_INTERVAL", timeout),
|
||||
RedisAddr: EnvString(pfix, "PUBSUB_REDIS_ADDR", "redis:6379"),
|
||||
RedisTimeout: EnvDuration(pfix, "PUBSUB_REDIS_TIMEOUT", timeout),
|
||||
RedisPingTimeout: EnvDuration(pfix, "PUBSUB_REDIS_PING_TIMEOUT", pingTimeout),
|
||||
RedisPingPeriod: EnvDuration(pfix, "PUBSUB_REDIS_PING_PERIOD", pingPeriod),
|
||||
Mode: "poll",
|
||||
PollingInterval: timeout,
|
||||
RedisAddr: "redis:6379",
|
||||
RedisTimeout: timeout,
|
||||
RedisPingTimeout: pingTimeout,
|
||||
RedisPingPeriod: pingPeriod,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+12
-10
@@ -2,22 +2,24 @@ package options
|
||||
|
||||
type (
|
||||
SMTPOpt struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Pass string
|
||||
From string
|
||||
Host string `env:"SMTP_HOST"`
|
||||
Port int `env:"SMTP_PORT"`
|
||||
User string `env:"SMTP_USERNAM"`
|
||||
Pass string `env:"SMTP_PASS"`
|
||||
From string `env:"SMTP_FROM"`
|
||||
}
|
||||
)
|
||||
|
||||
func SMTP(pfix string) (o *SMTPOpt) {
|
||||
o = &SMTPOpt{
|
||||
Host: EnvString(pfix, "SMTP_HOST", "localhost:25"),
|
||||
Port: EnvInt(pfix, "SMTP_PORT", 25),
|
||||
User: EnvString(pfix, "SMTP_USERNAME", ""),
|
||||
Pass: EnvString(pfix, "SMTP_PASS", ""),
|
||||
From: EnvString(pfix, "SMTP_FROM", ""),
|
||||
Host: "localhost:25",
|
||||
Port: 25,
|
||||
User: "",
|
||||
Pass: "",
|
||||
From: "",
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
type (
|
||||
WebsocketOpt struct {
|
||||
Timeout time.Duration
|
||||
PingTimeout time.Duration
|
||||
PingPeriod time.Duration
|
||||
Timeout time.Duration `env:"WEBSOCKET_TIMEOUT"`
|
||||
PingTimeout time.Duration `env:"WEBSOCKET_PING_TIMEOUT"`
|
||||
PingPeriod time.Duration `env:"WEBSOCKET_PING_PERIOD"`
|
||||
}
|
||||
)
|
||||
|
||||
@@ -20,10 +20,12 @@ func Websocket(pfix string) (o *WebsocketOpt) {
|
||||
)
|
||||
|
||||
o = &WebsocketOpt{
|
||||
Timeout: EnvDuration(pfix, "WEBSOCKET_TIMEOUT", timeout),
|
||||
PingTimeout: EnvDuration(pfix, "WEBSOCKET_PING_TIMEOUT", pingTimeout),
|
||||
PingPeriod: EnvDuration(pfix, "WEBSOCKET_PING_PERIOD", pingPeriod),
|
||||
Timeout: timeout,
|
||||
PingTimeout: pingTimeout,
|
||||
PingPeriod: pingPeriod,
|
||||
}
|
||||
|
||||
fill(o, pfix)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+12
-11
@@ -173,11 +173,20 @@ func (c *Config) Init() {
|
||||
c.DatabaseName = c.ServiceName
|
||||
}
|
||||
|
||||
c.LogOpt = options.Log(c.EnvPrefix)
|
||||
c.SmtpOpt = options.SMTP(c.EnvPrefix)
|
||||
c.JwtOpt = options.JWT(c.EnvPrefix)
|
||||
c.HttpClientOpt = options.HttpClient(c.EnvPrefix)
|
||||
c.DbOpt = options.DB(c.ServiceName)
|
||||
c.ProvisionOpt = options.Provision(c.ServiceName)
|
||||
|
||||
if c.RootCommandDBSetup == nil {
|
||||
c.RootCommandDBSetup = Runners{func(ctx context.Context, cmd *cobra.Command, c *Config) (err error) {
|
||||
_, err = db.TryToConnect(ctx, c.Log, c.DatabaseName, c.DbOpt.DSN, c.DbOpt.Profiler)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not connect to database")
|
||||
if c.DbOpt != nil {
|
||||
_, err = db.TryToConnect(ctx, c.Log, c.DatabaseName, c.DbOpt.DSN, c.DbOpt.Profiler)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not connect to database")
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
@@ -204,16 +213,8 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
|
||||
Use: c.RootCommandName,
|
||||
TraverseChildren: true,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
c.LogOpt = options.Log(c.EnvPrefix)
|
||||
c.SmtpOpt = options.SMTP(c.EnvPrefix)
|
||||
c.JwtOpt = options.JWT(c.EnvPrefix)
|
||||
c.HttpClientOpt = options.HttpClient(c.EnvPrefix)
|
||||
|
||||
InitGeneralServices(c.LogOpt, c.SmtpOpt, c.JwtOpt, c.HttpClientOpt)
|
||||
|
||||
c.DbOpt = options.DB(c.ServiceName)
|
||||
c.ProvisionOpt = options.Provision(c.ServiceName)
|
||||
|
||||
err = c.RootCommandDBSetup.Run(ctx, cmd, c)
|
||||
if err != nil {
|
||||
c.Log.Error("Failed to connect to the database", zap.Error(err))
|
||||
|
||||
Reference in New Issue
Block a user