Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not skip an inaccessible database at agent startup #46

Merged
merged 7 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deploy/demo-lab/.env
Original file line number Diff line number Diff line change
Expand Up @@ -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.*$"
Expand Down
1 change: 1 addition & 0 deletions deploy/demo-lab/pgscv/pgscv.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
38 changes: 26 additions & 12 deletions internal/pgscv/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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{}
Expand Down Expand Up @@ -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":
Expand Down Expand Up @@ -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 == "" {
Expand Down
37 changes: 21 additions & 16 deletions internal/pgscv/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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,
},
},
{
Expand Down
4 changes: 3 additions & 1 deletion internal/pgscv/pgscv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 {
Expand Down
19 changes: 12 additions & 7 deletions internal/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type Config struct {
CollectTopTable int
CollectTopIndex int
CollectTopQuery int
SkipConnErrorMode bool
}

// Collector is an interface for prometheus.Collector.
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down
35 changes: 27 additions & 8 deletions internal/service/service_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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,
},
{
Expand All @@ -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,
},
}
Expand Down