diff --git a/.env.example b/.env.example index 445b8dd84..fc3222d08 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,8 @@ -# When running as monolith/bundle +# Corteza configuration + +# All variables can be prefixed with _ (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 diff --git a/Dockerfile.corteza-server b/Dockerfile.corteza-server index 0af33dc1c..4f1308184 100644 --- a/Dockerfile.corteza-server +++ b/Dockerfile.corteza-server @@ -19,4 +19,4 @@ EXPOSE 80 ENTRYPOINT ["/bin/corteza-server"] # Forcing exposed port -CMD ["serve-api", "--http-addr=:80"] +CMD ["serve-api"] diff --git a/Dockerfile.corteza-server-compose b/Dockerfile.corteza-server-compose index 6d90d6db3..5b261de89 100644 --- a/Dockerfile.corteza-server-compose +++ b/Dockerfile.corteza-server-compose @@ -19,5 +19,5 @@ EXPOSE 80 ENTRYPOINT ["/bin/corteza-server-compose"] # Forcing exposed port -CMD ["serve-api", "--compose-http-addr=:80"] +CMD ["serve-api"] diff --git a/Dockerfile.corteza-server-messaging b/Dockerfile.corteza-server-messaging index ba2d7dbc8..dd05cdb1e 100644 --- a/Dockerfile.corteza-server-messaging +++ b/Dockerfile.corteza-server-messaging @@ -19,5 +19,5 @@ EXPOSE 80 ENTRYPOINT ["/bin/corteza-server-messaging"] # Forcing exposed port -CMD ["serve-api", "--messaging-http-addr=:80"] +CMD ["serve-api"] diff --git a/Dockerfile.corteza-server-system b/Dockerfile.corteza-server-system index e4a1a099c..3c13a0af8 100644 --- a/Dockerfile.corteza-server-system +++ b/Dockerfile.corteza-server-system @@ -19,5 +19,5 @@ EXPOSE 80 ENTRYPOINT ["/bin/corteza-server-system"] # Forcing exposed port -CMD ["serve-api", "--system-http-addr=:80"] +CMD ["serve-api"] diff --git a/README.md b/README.md index 16bf5e117..bdee6df10 100644 --- a/README.md +++ b/README.md @@ -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`. diff --git a/docs/CLI.md b/docs/CLI.md index 58b930aa7..bc6e50dc9 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -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: diff --git a/messaging/messaging.go b/messaging/messaging.go index 83c7b4a03..36b827361 100644 --- a/messaging/messaging.go +++ b/messaging/messaging.go @@ -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) diff --git a/monolith/monolith.go b/monolith/monolith.go index a46cc4029..4cfcf7f2d 100644 --- a/monolith/monolith.go +++ b/monolith/monolith.go @@ -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) diff --git a/pkg/api/server.go b/pkg/api/server.go index 3f3794a85..09af17b57 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -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...) } diff --git a/pkg/cli/flags/db.go b/pkg/cli/flags/db.go deleted file mode 100644 index 20908bd9f..000000000 --- a/pkg/cli/flags/db.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/flags.go b/pkg/cli/flags/flags.go deleted file mode 100644 index 1084f0227..000000000 --- a/pkg/cli/flags/flags.go +++ /dev/null @@ -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) -} diff --git a/pkg/cli/flags/http.go b/pkg/cli/flags/http.go deleted file mode 100644 index 6cffaae8f..000000000 --- a/pkg/cli/flags/http.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/http_client.go b/pkg/cli/flags/http_client.go deleted file mode 100644 index 137b067fb..000000000 --- a/pkg/cli/flags/http_client.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/jwt.go b/pkg/cli/flags/jwt.go deleted file mode 100644 index 37dea3637..000000000 --- a/pkg/cli/flags/jwt.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/log.go b/pkg/cli/flags/log.go deleted file mode 100644 index b7eca2f87..000000000 --- a/pkg/cli/flags/log.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/monitor.go b/pkg/cli/flags/monitor.go deleted file mode 100644 index 864aa75b6..000000000 --- a/pkg/cli/flags/monitor.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/provision.go b/pkg/cli/flags/provision.go deleted file mode 100644 index 59fdce918..000000000 --- a/pkg/cli/flags/provision.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/pubsub.go b/pkg/cli/flags/pubsub.go deleted file mode 100644 index cbcbb755d..000000000 --- a/pkg/cli/flags/pubsub.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/smtp.go b/pkg/cli/flags/smtp.go deleted file mode 100644 index 7a1287ab0..000000000 --- a/pkg/cli/flags/smtp.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/flags/websocket.go b/pkg/cli/flags/websocket.go deleted file mode 100644 index dfd5b9d88..000000000 --- a/pkg/cli/flags/websocket.go +++ /dev/null @@ -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 -} diff --git a/pkg/cli/helpers.go b/pkg/cli/helpers.go index 688675f31..0f972aba9 100644 --- a/pkg/cli/helpers.go +++ b/pkg/cli/helpers.go @@ -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) diff --git a/pkg/cli/options/db.go b/pkg/cli/options/db.go new file mode 100644 index 000000000..af116d76a --- /dev/null +++ b/pkg/cli/options/db.go @@ -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 +} diff --git a/pkg/cli/options/helpers.go b/pkg/cli/options/helpers.go new file mode 100644 index 000000000..b16f7a753 --- /dev/null +++ b/pkg/cli/options/helpers.go @@ -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 +} diff --git a/pkg/cli/options/http.go b/pkg/cli/options/http.go new file mode 100644 index 000000000..d5119cb00 --- /dev/null +++ b/pkg/cli/options/http.go @@ -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 +} diff --git a/pkg/cli/options/http_client.go b/pkg/cli/options/http_client.go new file mode 100644 index 000000000..482ca1881 --- /dev/null +++ b/pkg/cli/options/http_client.go @@ -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 +} diff --git a/pkg/cli/options/jwt.go b/pkg/cli/options/jwt.go new file mode 100644 index 000000000..ee0beef14 --- /dev/null +++ b/pkg/cli/options/jwt.go @@ -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 +} diff --git a/pkg/cli/options/log.go b/pkg/cli/options/log.go new file mode 100644 index 000000000..a5dfddfff --- /dev/null +++ b/pkg/cli/options/log.go @@ -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 +} diff --git a/pkg/cli/options/monitor.go b/pkg/cli/options/monitor.go new file mode 100644 index 000000000..e25a2eb07 --- /dev/null +++ b/pkg/cli/options/monitor.go @@ -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 +} diff --git a/pkg/cli/options/provision.go b/pkg/cli/options/provision.go new file mode 100644 index 000000000..078e4f8ec --- /dev/null +++ b/pkg/cli/options/provision.go @@ -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 +} diff --git a/pkg/cli/options/pubsub.go b/pkg/cli/options/pubsub.go new file mode 100644 index 000000000..7a6349d74 --- /dev/null +++ b/pkg/cli/options/pubsub.go @@ -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 +} diff --git a/pkg/cli/options/smtp.go b/pkg/cli/options/smtp.go new file mode 100644 index 000000000..9a5f68c04 --- /dev/null +++ b/pkg/cli/options/smtp.go @@ -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 +} diff --git a/pkg/cli/options/websocket.go b/pkg/cli/options/websocket.go new file mode 100644 index 000000000..9df3108a4 --- /dev/null +++ b/pkg/cli/options/websocket.go @@ -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 +} diff --git a/pkg/cli/runner.go b/pkg/cli/runner.go index fed9f6f42..6c7a0e75a 100644 --- a/pkg/cli/runner.go +++ b/pkg/cli/runner.go @@ -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-" 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 {