diff --git a/deploy/demo-lab/.env b/deploy/demo-lab/.env index 4373bfe..53de3d9 100644 --- a/deploy/demo-lab/.env +++ b/deploy/demo-lab/.env @@ -10,6 +10,7 @@ LOG_LEVEL=debug #PGSCV_COLLECT_TOP_QUERY=15 #PGSCV_COLLECT_TOP_TABLE=15 #PGSCV_COLLECT_TOP_INDEX=15 +#PGSCV_SKIP_CONN_ERROR_MODE=t #POSTGRES_DSN_db1=postgresql://pgscv:pgscv@host1:5432/pgbench1 #POSTGRES_DSN_db2=postgresql://pgscv:pgscv@host1:5432/pgbench2 #PGSCV_DATABASES="^pgbench.*$" diff --git a/deploy/demo-lab/pgscv/pgscv.yaml b/deploy/demo-lab/pgscv/pgscv.yaml index d21b4ab..9626b05 100644 --- a/deploy/demo-lab/pgscv/pgscv.yaml +++ b/deploy/demo-lab/pgscv/pgscv.yaml @@ -8,6 +8,7 @@ no_track_mode: false #collect_top_query: 10 #collect_top_table: 10 #collect_top_index: 10 +#skip_conn_error_mode: true services: "postgres12": service_type: "postgres" diff --git a/internal/pgscv/config.go b/internal/pgscv/config.go index 19018e4..015bc3a 100644 --- a/internal/pgscv/config.go +++ b/internal/pgscv/config.go @@ -35,11 +35,11 @@ type Config struct { CollectorsSettings model.CollectorsSettings `yaml:"collectors"` // Collectors settings propagated from main YAML configuration Databases string `yaml:"databases"` // Regular expression string specifies databases from which metrics should be collected DatabasesRE *regexp.Regexp // Regular expression object compiled from Databases - AuthConfig http.AuthConfig `yaml:"authentication"` // TLS and Basic auth configuration - CollectTopTable int `yaml:"collect_top_table"` // Limit elements on Table collector - CollectTopIndex int `yaml:"collect_top_index"` // Limit elements on Indexes collector - CollectTopQuery int `yaml:"collect_top_query"` // Limit elements on Statements collector - + AuthConfig http.AuthConfig `yaml:"authentication"` // TLS and Basic auth configuration + CollectTopTable int `yaml:"collect_top_table"` // Limit elements on Table collector + CollectTopIndex int `yaml:"collect_top_index"` // Limit elements on Indexes collector + CollectTopQuery int `yaml:"collect_top_query"` // Limit elements on Statements collector + SkipConnErrorMode bool `yaml:"skip_conn_error_mode"` // Skipping connection errors and creating a Service instance. } // NewConfig creates new config based on config file or return default config if config file is not specified. @@ -109,6 +109,9 @@ func NewConfig(configFilePath string) (*Config, error) { if configFromEnv.CollectTopQuery > 0 { configFromFile.CollectTopQuery = configFromEnv.CollectTopQuery } + if configFromEnv.SkipConnErrorMode { + configFromFile.SkipConnErrorMode = configFromEnv.SkipConnErrorMode + } return configFromFile, nil } @@ -179,6 +182,10 @@ func (c *Config) Validate() error { log.Infoln("no-track disabled, for details check the documentation about 'no_track_mode' option.") } + if c.SkipConnErrorMode { + log.Infoln("skipping connection errors is enabled.") + } + // setup defaults if c.Defaults == nil { c.Defaults = map[string]string{} @@ -387,12 +394,7 @@ func newConfigFromEnv() (*Config, error) { case "PGSCV_LISTEN_ADDRESS": config.ListenAddress = value case "PGSCV_NO_TRACK_MODE": - switch value { - case "y", "yes", "Yes", "YES", "t", "true", "True", "TRUE", "1", "on": - config.NoTrackMode = true - default: - config.NoTrackMode = false - } + config.NoTrackMode = toBool(value) case "PGSCV_DATABASES": config.Databases = value case "PGSCV_DISABLE_COLLECTORS": @@ -423,12 +425,24 @@ func newConfigFromEnv() (*Config, error) { return nil, fmt.Errorf("invalid setting PGSCV_COLLECT_TOP_INDEX, value '%s', allowed only digits", value) } config.CollectTopIndex = collectTopIndex + case "PGSCV_SKIP_CONN_ERROR_MODE": + config.SkipConnErrorMode = toBool(value) } } - return config, nil } +func toBool(s string) bool { + switch s { + case "y", "yes", "Yes", "YES", "t", "true", "True", "TRUE", "1", "on": + return true + case "n", "no", "No", "NO", "f", "false", "False", "FALSE", "0", "off": + return false + default: + return false + } +} + // newDatabasesRegexp creates new regexp depending on passed string. func newDatabasesRegexp(s string) (*regexp.Regexp, error) { if s == "" { diff --git a/internal/pgscv/config_test.go b/internal/pgscv/config_test.go index 4e99ad2..c7ce928 100644 --- a/internal/pgscv/config_test.go +++ b/internal/pgscv/config_test.go @@ -546,25 +546,29 @@ func Test_newConfigFromEnv(t *testing.T) { { valid: true, // No env variables envvars: map[string]string{}, - want: &Config{Defaults: map[string]string{}, ServicesConnsSettings: map[string]service.ConnSetting{}}, + want: &Config{ + Defaults: map[string]string{}, + ServicesConnsSettings: map[string]service.ConnSetting{}, + }, }, { valid: true, // Completely valid variables envvars: map[string]string{ - "PGSCV_LISTEN_ADDRESS": "127.0.0.1:12345", - "PGSCV_NO_TRACK_MODE": "yes", - "PGSCV_DATABASES": "exampledb", - "PGSCV_DISABLE_COLLECTORS": "example/1,example/2, example/3", - "POSTGRES_DSN": "example_dsn", - "POSTGRES_DSN_EXAMPLE1": "example_dsn", - "PGBOUNCER_DSN": "example_dsn", - "PGBOUNCER_DSN_EXAMPLE2": "example_dsn", - "PATRONI_URL": "example_url", - "PATRONI_URL_EXAMPLE3": "example_url", - "PGSCV_AUTH_USERNAME": "user", - "PGSCV_AUTH_PASSWORD": "pass", - "PGSCV_AUTH_KEYFILE": "keyfile.key", - "PGSCV_AUTH_CERTFILE": "certfile.cert", + "PGSCV_LISTEN_ADDRESS": "127.0.0.1:12345", + "PGSCV_NO_TRACK_MODE": "yes", + "PGSCV_DATABASES": "exampledb", + "PGSCV_DISABLE_COLLECTORS": "example/1,example/2, example/3", + "POSTGRES_DSN": "example_dsn", + "POSTGRES_DSN_EXAMPLE1": "example_dsn", + "PGBOUNCER_DSN": "example_dsn", + "PGBOUNCER_DSN_EXAMPLE2": "example_dsn", + "PATRONI_URL": "example_url", + "PATRONI_URL_EXAMPLE3": "example_url", + "PGSCV_AUTH_USERNAME": "user", + "PGSCV_AUTH_PASSWORD": "pass", + "PGSCV_AUTH_KEYFILE": "keyfile.key", + "PGSCV_AUTH_CERTFILE": "certfile.cert", + "PGSCV_SKIP_CONN_ERROR_MODE": "yes", }, want: &Config{ ListenAddress: "127.0.0.1:12345", @@ -585,7 +589,8 @@ func Test_newConfigFromEnv(t *testing.T) { Keyfile: "keyfile.key", Certfile: "certfile.cert", }, - Defaults: map[string]string{}, + Defaults: map[string]string{}, + SkipConnErrorMode: true, }, }, { diff --git a/internal/pgscv/pgscv.go b/internal/pgscv/pgscv.go index 867c4d1..a8d9c41 100644 --- a/internal/pgscv/pgscv.go +++ b/internal/pgscv/pgscv.go @@ -3,10 +3,11 @@ package pgscv import ( "context" "errors" + "sync" + "github.com/cherts/pgscv/internal/http" "github.com/cherts/pgscv/internal/log" "github.com/cherts/pgscv/internal/service" - "sync" ) // Start is the application's starting point. @@ -25,6 +26,7 @@ func Start(ctx context.Context, config *Config) error { CollectTopTable: config.CollectTopTable, CollectTopIndex: config.CollectTopIndex, CollectTopQuery: config.CollectTopQuery, + SkipConnErrorMode: config.SkipConnErrorMode, } if len(config.ServicesConnsSettings) == 0 { diff --git a/internal/service/service.go b/internal/service/service.go index fcf01d5..8caedf3 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -45,6 +45,7 @@ type Config struct { CollectTopTable int CollectTopIndex int CollectTopQuery int + SkipConnErrorMode bool } // Collector is an interface for prometheus.Collector. @@ -128,7 +129,7 @@ func (repo *Repository) addServicesFromConfig(config Config) { return } - // Check all passed connection settings and try to connect using them. In case of success, create a 'Service' instance + // Check all passed connection settings and try to connect using them. Create a 'Service' instance // in the repo. for k, cs := range config.ConnsSettings { var msg string @@ -154,15 +155,19 @@ func (repo *Repository) addServicesFromConfig(config Config) { // Check connection using created *ConnConfig, go next if connection failed. db, err := store.NewWithConfig(pgconfig) if err != nil { - log.Warnf("%s: %s, skip", cs.Conninfo, err) - continue + if config.SkipConnErrorMode { + log.Warnf("%s: %s", cs.Conninfo, err) + } else { + log.Warnf("%s: %s skip", cs.Conninfo, err) + continue + } + } else { + msg = fmt.Sprintf("service [%s] available through: %s@%s:%d/%s", k, pgconfig.User, pgconfig.Host, pgconfig.Port, pgconfig.Database) + db.Close() } - db.Close() - - msg = fmt.Sprintf("service [%s] available through: %s@%s:%d/%s", k, pgconfig.User, pgconfig.Host, pgconfig.Port, pgconfig.Database) } - // Connection was successful, create 'Service' struct with service-related properties and add it to service repo. + // Create 'Service' struct with service-related properties and add it to service repo. s := Service{ ServiceID: k, ConnSettings: cs, diff --git a/internal/service/service_test.go b/internal/service/service_test.go index 63e9033..013a9a5 100644 --- a/internal/service/service_test.go +++ b/internal/service/service_test.go @@ -1,10 +1,11 @@ package service import ( + "testing" + "github.com/cherts/pgscv/internal/model" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" - "testing" ) func TestRepository_addService(t *testing.T) { @@ -62,9 +63,22 @@ func TestRepository_addServicesFromConfig(t *testing.T) { }{ { name: "valid", - config: Config{ConnsSettings: ConnsSettings{ - "test": {ServiceType: model.ServiceTypePostgresql, Conninfo: "host=127.0.0.1 port=5432 user=pgscv dbname=pgscv_fixtures"}, - }}, + config: Config{ + SkipConnErrorMode: false, + ConnsSettings: ConnsSettings{ + "test": {ServiceType: model.ServiceTypePostgresql, + Conninfo: "host=127.0.0.1 port=5432 user=pgscv dbname=pgscv_fixtures"}, + }}, + expected: 2, + }, + { + name: "valid_skip_error", + config: Config{ + SkipConnErrorMode: true, + ConnsSettings: ConnsSettings{ + "test": {ServiceType: model.ServiceTypePostgresql, + Conninfo: "host=127.0.0.1 port=5430 user=pgscv dbname=pgscv_fixtures"}, + }}, expected: 2, }, { @@ -73,13 +87,18 @@ func TestRepository_addServicesFromConfig(t *testing.T) { expected: 1, }, { - name: "invalid service", - config: Config{ConnsSettings: ConnsSettings{"test": {ServiceType: model.ServiceTypePostgresql, Conninfo: "invalid conninfo"}}}, + name: "invalid service", + config: Config{ + SkipConnErrorMode: true, + ConnsSettings: ConnsSettings{"test": {ServiceType: model.ServiceTypePostgresql, Conninfo: "invalid conninfo"}}}, expected: 1, }, { - name: "unavailable service", - config: Config{ConnsSettings: ConnsSettings{"test": {ServiceType: model.ServiceTypePostgresql, Conninfo: "port=1"}}}, + name: "unavailable service", + config: Config{ + SkipConnErrorMode: false, + ConnsSettings: ConnsSettings{"test": {ServiceType: model.ServiceTypePostgresql, Conninfo: "port=1"}}, + }, expected: 1, }, }