3
0

Remove flags, improve ENV handling

Following https://12factor.net/config, Corteza internals are now configurable
ONLT via environmental variables.

See .env.example for details
This commit is contained in:
Denis Arh
2019-05-29 23:47:06 +02:00
parent ba1118eb4f
commit 8592aa8d35
34 changed files with 364 additions and 691 deletions

View File

@@ -1,4 +1,8 @@
# When running as monolith/bundle
# Corteza configuration
# All variables can be prefixed with <APP>_ (MESSAGING_, SYSTEM_, _COMPOSE) to
# achieve different behaviour for a specific app.
# Note: default docker image without any extra command will
# force :80 via flag and override anything you set here
HTTP_ADDR=:80
@@ -32,152 +36,11 @@ HTTP_CLIENT_TIMEOUT=30s
# Allow usage of insecure hosts (self-signed, expired certificates)
HTTP_CLIENT_TSL_INSECURE=false
########################################################################################################################
# When running separate services (microservice-like architecture)
#
# Prefixed ENV variables allow you to keep the same .env file for all services
# Log HTTP requests
HTTP_LOG_REQUESTS=true
#-----------------------------------------------------------------------------------------------------------------------
# System
# Monitoring log interval
MONITOR_INTERVAL=5min
# Database to use
SYSTEM_DB_DSN=corteza:corteza@tcp(localhost:3306)/corteza?collation=utf8mb4_general_ci
# Database query profiler
SYSTEM_DB_PROFILER=stdout
# Enable provisioning on startup: migrate database
SYSTEM_PROVISION_MIGRATE_DATABASE=true
# Enable provisioning on startup: auto-setup
SYSTEM_PROVISION_AUTO_SETUP=true
# System specific settings:
SYSTEM_HTTP_ADDR=:80
# Enable /debug route
SYSTEM_HTTP_ENABLE_DEBUG_ROUTE=true
# Enable /version route
SYSTEM_HTTP_ENABLE_VERSION_ROUTE=true
# Add stack trace on error to HTTP response
SYSTEM_HTTP_ENABLE_ERROR_TRACKING=true
# Log HTTP requests
SYSTEM_HTTP_LOG=true
# Serve prometheus metrics under /metrics
SYSTEM_HTTP_METRICS=true
# Metrics service label
SYSTEM_HTTP_METRICS_NAME=corteza
# Metrics access username
SYSTEM_HTTP_METRICS_USERNAME=metrics
# Metrics access password
SYSTEM_HTTP_METRICS_PASSWORD=metrics
# Log interval for monitoring
SYSTEM_MONITOR_INTERVAL=5min
#-----------------------------------------------------------------------------------------------------------------------
# Messaging
# Database to use
SYSTEM_DB_DSN=corteza:corteza@tcp(localhost:3306)/corteza?collation=utf8mb4_general_ci
# Database query profiler
SYSTEM_DB_PROFILER=stdout
# Enable provisioning on startup: migrate database
SYSTEM_PROVISION_MIGRATE_DATABASE=true
# Enable provisioning on startup: auto-setup
SYSTEM_PROVISION_AUTO_SETUP=true
# System specific settings:
SYSTEM_HTTP_ADDR=:80
# Enable /debug route
SYSTEM_HTTP_ENABLE_DEBUG_ROUTE=true
# Enable /version route
SYSTEM_HTTP_ENABLE_VERSION_ROUTE=true
# Add stack trace on error to HTTP response
SYSTEM_HTTP_ENABLE_ERROR_TRACKING=true
# Log HTTP requests
SYSTEM_HTTP_LOG=true
# Serve prometheus metrics under /metrics
SYSTEM_HTTP_METRICS=true
# Metrics service label
SYSTEM_HTTP_METRICS_NAME=corteza
# Metrics access username
SYSTEM_HTTP_METRICS_USERNAME=metrics
# Metrics access password
SYSTEM_HTTP_METRICS_PASSWORD=metrics
# Log interval for monitoring
SYSTEM_MONITOR_INTERVAL=5min
# Websocket connection ping period
# MESSAGING_WEBSOCKET_PING_PERIOD=
# Websocket connection ping timeout
# MESSAGING_WEBSOCKET_PING_TIMEOUT=
# Websocket connection timeout
# MESSAGING_WEBSOCKET_TIMEOUT=
#-----------------------------------------------------------------------------------------------------------------------
# System
# Database to use
SYSTEM_DB_DSN=corteza:corteza@tcp(localhost:3306)/corteza?collation=utf8mb4_general_ci
# Database query profiler
SYSTEM_DB_PROFILER=stdout
# Enable provisioning on startup: migrate database
SYSTEM_PROVISION_MIGRATE_DATABASE=true
# Enable provisioning on startup: auto-setup
SYSTEM_PROVISION_AUTO_SETUP=true
# System specific settings:
SYSTEM_HTTP_ADDR=:80
# Enable /debug route
SYSTEM_HTTP_ENABLE_DEBUG_ROUTE=true
# Enable /version route
SYSTEM_HTTP_ENABLE_VERSION_ROUTE=true
# Add stack trace on error to HTTP response
SYSTEM_HTTP_ENABLE_ERROR_TRACKING=true
# Log HTTP requests
SYSTEM_HTTP_LOG=true
# Serve prometheus metrics under /metrics
SYSTEM_HTTP_METRICS=true
# Metrics service label
SYSTEM_HTTP_METRICS_NAME=corteza
# Metrics access username
SYSTEM_HTTP_METRICS_USERNAME=metrics
# Metrics access password
SYSTEM_HTTP_METRICS_PASSWORD=metrics
# Log interval for monitoring
SYSTEM_MONITOR_INTERVAL=5min
DB_DSN=corteza:corteza@tcp(localhost:3306)/corteza?collation=utf8mb4_general_ci

View File

@@ -19,4 +19,4 @@ EXPOSE 80
ENTRYPOINT ["/bin/corteza-server"]
# Forcing exposed port
CMD ["serve-api", "--http-addr=:80"]
CMD ["serve-api"]

View File

@@ -19,5 +19,5 @@ EXPOSE 80
ENTRYPOINT ["/bin/corteza-server-compose"]
# Forcing exposed port
CMD ["serve-api", "--compose-http-addr=:80"]
CMD ["serve-api"]

View File

@@ -19,5 +19,5 @@ EXPOSE 80
ENTRYPOINT ["/bin/corteza-server-messaging"]
# Forcing exposed port
CMD ["serve-api", "--messaging-http-addr=:80"]
CMD ["serve-api"]

View File

@@ -19,5 +19,5 @@ EXPOSE 80
ENTRYPOINT ["/bin/corteza-server-system"]
# Forcing exposed port
CMD ["serve-api", "--system-http-addr=:80"]
CMD ["serve-api"]

View File

@@ -24,22 +24,15 @@ Crust Unify manages the user experience for Crust applications, such as CRM, Mes
Copy `.env.example` to `.env` and make proper modifications for your local environment.
An access to a (local) instance of MySQL must be available.
Configure access to your database with `SYSTEM_DB_DSN`, `MESSAGING_DB_DSN` and `COMPOSE_DB_DSN`.
Configure access to your database with `DB_DSN`.
The database will be populated with migrations at the start of each service. You don't need to pre-populate the database, just make sure that your permissions include CREATE and ALTER capabilities.
# Running in local environment for development
Everything should be set and ready to run with `make realize`. This utilizes realize tool that monitors codebase for changes and restarts api http server for every file change. It is not 100% so it needs help (manual restart) in certain cases (new files added, changes in non .go files etc..)
Everything should be set and ready to run with `make realize`. This utilizes realize
tool that monitors codebase for changes and restarts api http server for every file change. It is not 100% so it needs help (manual restart) in certain cases (new files added, changes in non .go files etc..)
# Making changes
# Documentation:
Please refer to each project's style guidelines and guidelines for submitting patches and additions. In general, we follow the "fork-and-pull" Git workflow.
1. **Fork** the repo on GitHub
2. **Clone** the project to your own machine
3. **Commit** changes to your own branch
4. **Push** your work back up to your fork
5. Submit a **Pull request** so that we can review your changes
NOTE: Be sure to merge the latest master from "upstream" before making a pull request!
You can find more documentation about Corteza CLI, setup, authentication and API endpoints under `/docs`.

View File

@@ -13,13 +13,14 @@ When system is build into a all-in-one binary or image,
app-specific commands can be accessed under a sub-command with the
same name as the app.
## Flags & environmental variables
Command and application behaviour can be adjusted using flags and/or environmental
variables. Flags have highest priority and override env. variables. Defaults that
are pre-set internally are optimized for production mode.
## Config
Command and application behaviour can be adjusted using environmental
variables. Defaults that are pre-set internally are optimized for production mode.
Make sure you read `/.env.example` for all the available options.
Note that defaults shown under `--help` are overridden by value from the accompanying
environemntal variable.
environmental variable.
## How to run:

View File

@@ -13,7 +13,7 @@ import (
"github.com/cortezaproject/corteza-server/messaging/rest"
"github.com/cortezaproject/corteza-server/messaging/websocket"
"github.com/cortezaproject/corteza-server/pkg/cli"
"github.com/cortezaproject/corteza-server/pkg/cli/flags"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
)
const (
@@ -22,9 +22,6 @@ const (
func Configure() *cli.Config {
var (
// Messaging API Server specific
websocketOpt *flags.WebsocketOpt
// Websocket handler
ws *websocket.Websocket
@@ -47,6 +44,8 @@ func Configure() *cli.Config {
cli.HandleError(service.Init(ctx, c.Log))
var websocketOpt = options.Websocket(messaging)
ws = websocket.Init(ctx, &websocket.Config{
Timeout: websocketOpt.Timeout,
PingTimeout: websocketOpt.PingTimeout,
@@ -61,12 +60,6 @@ func Configure() *cli.Config {
},
},
ApiServerAdtFlags: cli.FlagBinders{
func(cmd *cobra.Command, c *cli.Config) {
websocketOpt = flags.Websocket(cmd, messaging)
},
},
ApiServerPreRun: cli.Runners{
func(ctx context.Context, cmd *cobra.Command, c *cli.Config) error {
go service.Watchers(ctx)

View File

@@ -22,10 +22,6 @@ func Configure() *cli.Config {
msg.Init()
sys.Init()
cmp.RootCommandBaseFlags = nil
msg.RootCommandBaseFlags = nil
sys.RootCommandBaseFlags = nil
// Combines all three services/apps and makes them run as one monolith app
return &cli.Config{
ServiceName: "",
@@ -49,20 +45,6 @@ func Configure() *cli.Config {
},
},
RootCommandPrefixedFlags: cli.FlagBinders{
func(cmd *cobra.Command, c *cli.Config) {
cmp.RootCommandPrefixedFlags.Bind(cmd, cmp)
msg.RootCommandPrefixedFlags.Bind(cmd, msg)
sys.RootCommandPrefixedFlags.Bind(cmd, sys)
},
},
ApiServerAdtFlags: cli.CombineFlagBinders(
cmp.ApiServerAdtFlags,
msg.ApiServerAdtFlags,
sys.ApiServerAdtFlags,
),
ApiServerRoutes: cli.Mounters{
func(r chi.Router) {
r.Route("/compose", cmp.ApiServerRoutes.MountRoutes)

View File

@@ -13,7 +13,7 @@ import (
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/version"
"github.com/cortezaproject/corteza-server/pkg/cli/flags"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
)
type (
@@ -22,8 +22,8 @@ type (
log *zap.Logger
httpOpt *flags.HTTPOpt
monitorOpt *flags.MonitorOpt
httpOpt *options.HTTPOpt
monitorOpt *options.MonitorOpt
endpoints []func(r chi.Router)
}
@@ -43,6 +43,9 @@ func (s *Server) Command(ctx context.Context, cmdName, prefix string, preRun fun
// Connect all the wires, prepare services, run watchers, bind endpoints
PreRunE: func(cmd *cobra.Command, args []string) error {
s.httpOpt = options.HTTP(prefix)
s.monitorOpt = options.Monitor(prefix)
if s.monitorOpt.Interval > 0 {
go NewMonitor(int(s.monitorOpt.Interval / time.Second))
}
@@ -56,15 +59,9 @@ func (s *Server) Command(ctx context.Context, cmdName, prefix string, preRun fun
},
}
s.BindApiServerFlags(cmd, prefix)
return
}
func (s *Server) BindApiServerFlags(cmd *cobra.Command, prefix string) {
s.httpOpt = flags.HTTP(cmd, prefix)
s.monitorOpt = flags.Monitor(cmd, prefix)
}
func (s *Server) MountRoutes(mm ...func(chi.Router)) {
s.endpoints = append(s.endpoints, mm...)
}

View File

@@ -1,26 +0,0 @@
package flags
import (
"github.com/spf13/cobra"
)
type (
DBOpt struct {
DSN string
Profiler string
}
)
func DB(cmd *cobra.Command, pfix string) (o *DBOpt) {
o = &DBOpt{}
BindString(cmd, &o.DSN,
pFlag(pfix, "db-dsn"), "corteza:corteza@tcp(db:3306)/corteza?collation=utf8mb4_general_ci",
"DSN for database connection")
BindString(cmd, &o.Profiler,
pFlag(pfix, "db-profiler"), "none",
"Profiler for DB queries (none, stdout, logger)")
return
}

View File

@@ -1,56 +0,0 @@
package flags
import (
"os"
"strings"
"time"
"github.com/spf13/cast"
"github.com/spf13/cobra"
)
// Prefixes flag
func pFlag(pfix, name string) string {
if pfix != "" {
name = pfix + "-" + name
}
return name
}
// Converts input (flag-name) into ENVIRONMENTAL_VARIABLE_KEY
func envKey(s string) string {
return strings.ToUpper(strings.ReplaceAll(s, "-", "_"))
}
func BindString(cmd *cobra.Command, v *string, flag, def string, desc string) {
if env, has := os.LookupEnv(envKey(flag)); has {
def = cast.ToString(env)
}
cmd.Flags().StringVar(v, flag, def, desc)
}
func BindBool(cmd *cobra.Command, v *bool, flag string, def bool, desc string) {
if env, has := os.LookupEnv(envKey(flag)); has {
def = cast.ToBool(env)
}
cmd.Flags().BoolVar(v, flag, def, desc)
}
func BindInt(cmd *cobra.Command, v *int, flag string, def int, desc string) {
if env, has := os.LookupEnv(envKey(flag)); has {
def = cast.ToInt(env)
}
cmd.Flags().IntVar(v, flag, def, desc)
}
func BindDuration(cmd *cobra.Command, v *time.Duration, flag string, def time.Duration, desc string) {
if env, has := os.LookupEnv(envKey(flag)); has {
def = cast.ToDuration(env)
}
cmd.Flags().DurationVar(v, flag, def, desc)
}

View File

@@ -1,70 +0,0 @@
package flags
import (
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/internal/rand"
)
type (
HTTPOpt struct {
Addr string
Logging bool
Tracing bool
EnableVersionRoute bool
EnableDebugRoute bool
EnableMetrics bool
MetricsServiceLabel string
MetricsUsername string
MetricsPassword string
}
)
func HTTP(cmd *cobra.Command, pfix string) (o *HTTPOpt) {
o = &HTTPOpt{}
BindString(cmd, &o.Addr,
pFlag(pfix, "http-addr"), ":80",
"Listen address for HTTP server")
BindBool(cmd, &o.Logging,
pFlag(pfix, "http-log"), true,
"Enable/disable HTTP request log")
BindBool(cmd, &o.Pretty,
pFlag(pfix, "http-pretty-json"), false,
"Prettify returned JSON output")
BindBool(cmd, &o.Tracing,
pFlag(pfix, "http-error-tracing"), false,
"Return error stack frame")
BindBool(cmd, &o.EnableVersionRoute,
pFlag(pfix, "http-enable-version-route"), true,
"Enable /version route")
BindBool(cmd, &o.EnableDebugRoute,
pFlag(pfix, "http-enable-debug-route"), false,
"Enable /debug route with pprof data")
BindBool(cmd, &o.EnableMetrics,
pFlag(pfix, "http-metrics"), false,
"Enable metrics")
BindString(cmd, &o.MetricsServiceLabel,
pFlag(pfix, "http-metrics-name"), "corteza",
"Provide metrics service label for Prometheus")
BindString(cmd, &o.MetricsUsername,
pFlag(pfix, "http-metrics-username"), "metrics",
"Provide metrics username for Prometheus")
// Setting metrics password to random string to prevent security accidents...
BindString(cmd, &o.MetricsPassword,
pFlag(pfix, "http-metrics-password"), string(rand.Bytes(5)),
"Provide metrics password for Prometheus")
return
}

View File

@@ -1,28 +0,0 @@
package flags
import (
"time"
"github.com/spf13/cobra"
)
type (
HttpClientOpt struct {
ClientTSLInsecure bool
HttpClientTimeout time.Duration
}
)
func HttpClient(cmd *cobra.Command) (o *HttpClientOpt) {
o = &HttpClientOpt{}
BindBool(cmd, &o.ClientTSLInsecure,
"http-client-tsl-insecure", false,
"Skip insecure TSL verification on outbound HTTP requests (allow invalid/self-signed certificates")
BindDuration(cmd, &o.HttpClientTimeout,
"http-client-timeout", 30*time.Second,
"Default HTTP client timeout")
return
}

View File

@@ -1,31 +0,0 @@
package flags
import (
"time"
"github.com/spf13/cobra"
"github.com/cortezaproject/corteza-server/internal/rand"
)
type (
JWTOpt struct {
Secret string
Expiry time.Duration
}
)
func JWT(cmd *cobra.Command) (o *JWTOpt) {
o = &JWTOpt{}
// Setting JWT secret to random string to prevent security accidents...
BindString(cmd, &o.Secret,
"auth-jwt-secret", string(rand.Bytes(32)),
"JWT Secret")
BindDuration(cmd, &o.Expiry,
"auth-jwt-expiry", time.Hour*24*30,
"JWT Expiration")
return
}

View File

@@ -1,23 +0,0 @@
package flags
import (
"github.com/spf13/cobra"
)
type (
// Logger's output leve is configured here, but
// dev/prod configuration happens earlier
LogOpt struct {
Level string
}
)
func Log(cmd *cobra.Command) (o *LogOpt) {
o = &LogOpt{}
BindString(cmd, &o.Level,
"log-level", "info",
"Log level (debug, info, warn, error, panic, fatal)")
return
}

View File

@@ -1,23 +0,0 @@
package flags
import (
"time"
"github.com/spf13/cobra"
)
type (
MonitorOpt struct {
Interval time.Duration
}
)
func Monitor(cmd *cobra.Command, pfix string) (o *MonitorOpt) {
o = &MonitorOpt{}
BindDuration(cmd, &o.Interval,
pFlag(pfix, "monitor-interval"), 300*time.Second,
"Monitor interval (0 = disable)")
return
}

View File

@@ -1,27 +0,0 @@
package flags
import (
"github.com/spf13/cobra"
)
type (
ProvisionOpt struct {
MigrateDatabase bool
AutoSetup bool
}
)
func Provision(cmd *cobra.Command, pfix string) (o *ProvisionOpt) {
o = &ProvisionOpt{}
BindBool(cmd, &o.MigrateDatabase,
pFlag(pfix, "provision-migrate-database"), true,
"Run database migration")
BindBool(cmd, &o.AutoSetup,
pFlag(pfix, "provision-auto-setup"), true,
"Run auto-setup procedures on service")
return
}

View File

@@ -1,58 +0,0 @@
package flags
import (
"time"
"github.com/spf13/cobra"
)
type (
PubSubOpt struct {
Mode string
// Mode
PollingInterval time.Duration
// Redis
RedisAddr string
RedisTimeout time.Duration
RedisPingTimeout time.Duration
RedisPingPeriod time.Duration
}
)
func PubSub(cmd *cobra.Command, pfix string) (o *PubSubOpt) {
o = &PubSubOpt{}
const (
timeout = 15 * time.Second
pingTimeout = 120 * time.Second
pingPeriod = (pingTimeout * 9) / 10
)
BindString(cmd, &o.Mode,
pFlag(pfix, "pubsub-mode"), "poll",
"Pub/Sub mode (poll, redis")
BindDuration(cmd, &o.RedisPingTimeout,
pFlag(pfix, "pubsub-polling-interval"), timeout,
"Sub/Sub polling interval")
BindString(cmd, &o.RedisAddr,
pFlag(pfix, "pubsub-redis-addr"), "redis:6379",
"Pub/Sub mode (poll, redis")
BindDuration(cmd, &o.RedisTimeout,
pFlag(pfix, "pubsub-redis-timeout"), timeout,
"Websocket connection timeout")
BindDuration(cmd, &o.RedisPingTimeout,
pFlag(pfix, "pubsub-redis-ping-timeout"), pingTimeout,
"Pub/Sub connection ping timeout")
BindDuration(cmd, &o.RedisPingPeriod,
pFlag(pfix, "pubsub-redis-ping-period"), pingPeriod,
"Pub/Sub connection ping period (should be lower than timeout)")
return
}

View File

@@ -1,41 +0,0 @@
package flags
import (
"github.com/spf13/cobra"
)
type (
SMTPOpt struct {
Host string
Port int
User string
Pass string
From string
}
)
func SMTP(cmd *cobra.Command) (o *SMTPOpt) {
o = &SMTPOpt{}
BindString(cmd, &o.Host,
"smtp-host", "localhost:25",
"SMTP hostname")
BindString(cmd, &o.User,
"smtp-username", "",
"SMTP server username")
BindString(cmd, &o.Pass,
"smtp-pass", "",
"SMTP server password")
BindString(cmd, &o.From,
"smtp-from", "",
"Sender's email address")
BindInt(cmd, &o.Port,
"smtp-port", 25,
"SMTP port number")
return
}

View File

@@ -1,39 +0,0 @@
package flags
import (
"time"
"github.com/spf13/cobra"
)
type (
WebsocketOpt struct {
Timeout time.Duration
PingTimeout time.Duration
PingPeriod time.Duration
}
)
func Websocket(cmd *cobra.Command, pfix string) (o *WebsocketOpt) {
o = &WebsocketOpt{}
const (
timeout = 15 * time.Second
pingTimeout = 120 * time.Second
pingPeriod = (pingTimeout * 9) / 10
)
BindDuration(cmd, &o.Timeout,
pFlag(pfix, "websocket-timeout"), timeout,
"Websocket connection timeout")
BindDuration(cmd, &o.PingTimeout,
pFlag(pfix, "websocket-ping-timeout"), pingTimeout,
"Websocket connection ping timeout")
BindDuration(cmd, &o.PingPeriod,
pFlag(pfix, "websocket-ping-period"), pingPeriod,
"Websocket connection ping period (should be lower than timeout)")
return
}

View File

@@ -10,11 +10,11 @@ import (
"github.com/cortezaproject/corteza-server/internal/auth"
"github.com/cortezaproject/corteza-server/internal/http"
"github.com/cortezaproject/corteza-server/internal/mail"
"github.com/cortezaproject/corteza-server/pkg/cli/flags"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
"github.com/cortezaproject/corteza-server/pkg/logger"
)
func InitGeneralServices(logOpt *flags.LogOpt, smtpOpt *flags.SMTPOpt, jwtOpt *flags.JWTOpt, httpClientOpt *flags.HttpClientOpt) {
func InitGeneralServices(logOpt *options.LogOpt, smtpOpt *options.SMTPOpt, jwtOpt *options.JWTOpt, httpClientOpt *options.HttpClientOpt) {
// Reset logger's level to whatever we want
var logLevel = zap.InfoLevel
_ = logLevel.Set(logOpt.Level)

17
pkg/cli/options/db.go Normal file
View File

@@ -0,0 +1,17 @@
package options
type (
DBOpt struct {
DSN string
Profiler string
}
)
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"),
}
return
}

View File

@@ -0,0 +1,58 @@
package options
import (
"os"
"strings"
"time"
"github.com/spf13/cast"
)
func makeEnvKeys(pfix, name string) []string {
return []string{
strings.ToUpper(strings.Trim(pfix, "_") + "_" + name),
strings.ToUpper(name),
}
}
func EnvString(pfix, key string, def string) string {
for _, key = range makeEnvKeys(pfix, key) {
if val, has := os.LookupEnv(key); has {
return val
}
}
return def
}
func EnvBool(pfix, key string, def bool) bool {
for _, key = range makeEnvKeys(pfix, key) {
if val, has := os.LookupEnv(key); has {
if b, err := cast.ToBoolE(val); err == nil {
return b
}
}
}
return def
}
func EnvInt(pfix, key string, def int) int {
for _, key = range makeEnvKeys(pfix, key) {
if val, has := os.LookupEnv(key); has {
if i, err := cast.ToIntE(val); err == nil {
return i
}
}
}
return def
}
func EnvDuration(pfix, key string, def time.Duration) time.Duration {
for _, key = range makeEnvKeys(pfix, key) {
if val, has := os.LookupEnv(key); has {
if d, err := cast.ToDurationE(val); err == nil {
return d
}
}
}
return def
}

39
pkg/cli/options/http.go Normal file
View File

@@ -0,0 +1,39 @@
package options
import (
"github.com/cortezaproject/corteza-server/internal/rand"
)
type (
HTTPOpt struct {
Addr string
Logging bool
Tracing bool
EnableVersionRoute bool
EnableDebugRoute bool
EnableMetrics bool
MetricsServiceLabel string
MetricsUsername string
MetricsPassword string
}
)
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"),
// Setting metrics password to random string to prevent security accidents...
MetricsPassword: EnvString(pfix, "HTTP_METRICS_PASSWORD", string(rand.Bytes(5))),
}
return
}

View File

@@ -0,0 +1,21 @@
package options
import (
"time"
)
type (
HttpClientOpt struct {
ClientTSLInsecure bool
HttpClientTimeout time.Duration
}
)
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),
}
return
}

24
pkg/cli/options/jwt.go Normal file
View File

@@ -0,0 +1,24 @@
package options
import (
"time"
"github.com/cortezaproject/corteza-server/internal/rand"
)
type (
JWTOpt struct {
Secret string
Expiry time.Duration
}
)
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),
}
return
}

17
pkg/cli/options/log.go Normal file
View File

@@ -0,0 +1,17 @@
package options
type (
// Logger's output leve is configured here, but
// dev/prod configuration happens earlier
LogOpt struct {
Level string
}
)
func Log(pfix string) (o *LogOpt) {
o = &LogOpt{
Level: EnvString(pfix, "LOG_LEVEL", "info"),
}
return
}

View File

@@ -0,0 +1,19 @@
package options
import (
"time"
)
type (
MonitorOpt struct {
Interval time.Duration
}
)
func Monitor(pfix string) (o *MonitorOpt) {
o = &MonitorOpt{
Interval: EnvDuration(pfix, "MONITOR_INTERVAL", 300*time.Second),
}
return
}

View File

@@ -0,0 +1,18 @@
package options
type (
ProvisionOpt struct {
MigrateDatabase bool
AutoSetup bool
}
)
func Provision(pfix string) (o *ProvisionOpt) {
o = &ProvisionOpt{
MigrateDatabase: EnvBool(pfix, "PROVISION_MIGRATE_DATABASE", true),
AutoSetup: EnvBool(pfix, "PROVISION_AUTO_SETUP", true),
}
return
}

39
pkg/cli/options/pubsub.go Normal file
View File

@@ -0,0 +1,39 @@
package options
import (
"time"
)
type (
PubSubOpt struct {
Mode string
// Mode
PollingInterval time.Duration
// Redis
RedisAddr string
RedisTimeout time.Duration
RedisPingTimeout time.Duration
RedisPingPeriod time.Duration
}
)
func PubSub(pfix string) (o *PubSubOpt) {
const (
timeout = 15 * time.Second
pingTimeout = 120 * time.Second
pingPeriod = (pingTimeout * 9) / 10
)
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),
}
return
}

23
pkg/cli/options/smtp.go Normal file
View File

@@ -0,0 +1,23 @@
package options
type (
SMTPOpt struct {
Host string
Port int
User string
Pass string
From string
}
)
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", ""),
}
return
}

View File

@@ -0,0 +1,29 @@
package options
import (
"time"
)
type (
WebsocketOpt struct {
Timeout time.Duration
PingTimeout time.Duration
PingPeriod time.Duration
}
)
func Websocket(pfix string) (o *WebsocketOpt) {
const (
timeout = 15 * time.Second
pingTimeout = 120 * time.Second
pingPeriod = (pingTimeout * 9) / 10
)
o = &WebsocketOpt{
Timeout: EnvDuration(pfix, "WEBSOCKET_TIMEOUT", timeout),
PingTimeout: EnvDuration(pfix, "WEBSOCKET_PING_TIMEOUT", pingTimeout),
PingPeriod: EnvDuration(pfix, "WEBSOCKET_PING_PERIOD", pingPeriod),
}
return
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/cortezaproject/corteza-server/internal/db"
"github.com/cortezaproject/corteza-server/pkg/api"
"github.com/cortezaproject/corteza-server/pkg/cli/flags"
"github.com/cortezaproject/corteza-server/pkg/cli/options"
"github.com/cortezaproject/corteza-server/pkg/logger"
)
@@ -34,19 +34,20 @@ type (
// See comments on other fields for how it is used.
ServiceName string
// Prefix for ENV variables
EnvPrefix string
// Logger name for internal services, defaults to ServiceName
LoggerName string
Log *zap.Logger
// General options/flags
LogOpt *flags.LogOpt
SmtpOpt *flags.SMTPOpt
JwtOpt *flags.JWTOpt
HttpClientOpt *flags.HttpClientOpt
// Per-service options/flags
DbOpt *flags.DBOpt
ProvisionOpt *flags.ProvisionOpt
// General options
LogOpt *options.LogOpt
SmtpOpt *options.SMTPOpt
JwtOpt *options.JWTOpt
HttpClientOpt *options.HttpClientOpt
DbOpt *options.DBOpt
ProvisionOpt *options.ProvisionOpt
// DB Connection name, defaults to ServiceName
DatabaseName string
@@ -54,15 +55,6 @@ type (
// Root command name, , defaults to "corteza-server-<ServiceName>"
RootCommandName string
// Flags that are bond to root command, no (per-service) prefixed
RootCommandBaseFlags FlagBinders
// Prefix for flags for root command, defaults to ServiceName
RootCommandFlagsPrefix string
// Flags that are bond to root command, (per-service) prefixed
RootCommandPrefixedFlags FlagBinders
// Database setup/connection procedure
// Runner autobinds default runner that tries to connect using DbOpt.DSN
RootCommandDBSetup Runners
@@ -78,12 +70,6 @@ type (
// API Server command name
ApiServerCommandName string
// Prefix for "serve-api command flags", defaults to ServiceName
ApiServerFlagsPrefix string
// Additional command flags for API server
ApiServerAdtFlags FlagBinders
// Code that needs to be executed before HTTP server is started
ApiServerPreRun Runners
@@ -169,6 +155,10 @@ func (c *Config) Init() {
c.LoggerName = c.ServiceName
}
if c.EnvPrefix == "" {
c.EnvPrefix = c.ServiceName
}
c.Log = c.Log.Named(c.LoggerName)
if c.RootCommandName == "" {
@@ -179,10 +169,6 @@ func (c *Config) Init() {
c.ApiServerCommandName = "serve-api"
}
if c.ApiServerFlagsPrefix == "" {
c.ApiServerFlagsPrefix = c.ServiceName
}
if c.DatabaseName == "" {
c.DatabaseName = c.ServiceName
}
@@ -198,32 +184,6 @@ func (c *Config) Init() {
}}
}
// Flags, not prefixed with service name
if c.RootCommandBaseFlags == nil {
c.RootCommandBaseFlags = FlagBinders{
func(cmd *cobra.Command, c *Config) {
c.LogOpt = flags.Log(cmd)
c.SmtpOpt = flags.SMTP(cmd)
c.JwtOpt = flags.JWT(cmd)
c.HttpClientOpt = flags.HttpClient(cmd)
},
}
}
if c.RootCommandFlagsPrefix == "" {
c.RootCommandFlagsPrefix = c.ServiceName
}
// Flags, prefixed with service name
if c.RootCommandPrefixedFlags == nil {
c.RootCommandPrefixedFlags = FlagBinders{
func(cmd *cobra.Command, c *Config) {
c.DbOpt = flags.DB(cmd, c.ServiceName)
c.ProvisionOpt = flags.Provision(cmd, c.ServiceName)
},
}
}
if c.ApiServer == nil {
c.ApiServer = api.NewServer(c.Log)
}
@@ -244,8 +204,16 @@ 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))
@@ -262,16 +230,10 @@ func (c *Config) MakeCLI(ctx context.Context) (cmd *cobra.Command) {
},
}
c.RootCommandBaseFlags.Bind(cmd, c)
c.RootCommandPrefixedFlags.Bind(cmd, c)
serveApiCmd := c.ApiServer.Command(ctx, c.ApiServerCommandName, c.ApiServerFlagsPrefix, func(ctx context.Context) (err error) {
serveApiCmd := c.ApiServer.Command(ctx, c.ApiServerCommandName, c.EnvPrefix, func(ctx context.Context) (err error) {
return c.ApiServerPreRun.Run(ctx, cmd, c)
})
// Bind all flags we need for serving the API
c.ApiServerAdtFlags.Bind(serveApiCmd, c)
cmd.AddCommand(serveApiCmd)
if len(c.ProvisionMigrateDatabase) > 0 || len(c.ProvisionAccessControl) > 0 {