...

Source file src/github.com/cybertec-postgresql/pgwatch/v3/internal/sources/types.go

Documentation: github.com/cybertec-postgresql/pgwatch/v3/internal/sources

     1  package sources
     2  
     3  import (
     4  	"fmt"
     5  	"maps"
     6  	"reflect"
     7  	"slices"
     8  
     9  	"github.com/jackc/pgx/v5"
    10  )
    11  
    12  type Kind string
    13  
    14  const (
    15  	SourcePostgres           Kind = "postgres"
    16  	SourcePostgresContinuous Kind = "postgres-continuous-discovery"
    17  	SourcePgBouncer          Kind = "pgbouncer"
    18  	SourcePgPool             Kind = "pgpool"
    19  	SourcePatroni            Kind = "patroni"
    20  	SourcePatroniContinuous  Kind = "patroni-continuous-discovery"
    21  	SourcePatroniNamespace   Kind = "patroni-namespace-discovery"
    22  )
    23  
    24  var Kinds = []Kind{
    25  	SourcePostgres,
    26  	SourcePostgresContinuous,
    27  	SourcePgBouncer,
    28  	SourcePgPool,
    29  	SourcePatroni,
    30  	SourcePatroniContinuous,
    31  	SourcePatroniNamespace,
    32  }
    33  
    34  func (k Kind) IsValid() bool {
    35  	return slices.Contains(Kinds, k)
    36  }
    37  
    38  type (
    39  
    40  	// Source represents a configuration how to get databases to monitor. It can be a single database,
    41  	// a group of databases in postgres cluster, a group of databases in HA patroni cluster.
    42  	// pgbouncer and pgpool kinds are purely to indicate that the monitored database connection is made
    43  	// through a connection pooler, which supports its own additional metrics. If one is not interested in
    44  	// those additional metrics, it is ok to specify the connection details as a regular postgres source.
    45  	Source struct {
    46  		Name                 string             `yaml:"name" db:"name"`
    47  		Group                string             `yaml:"group" db:"group"`
    48  		ConnStr              string             `yaml:"conn_str" db:"connstr"`
    49  		Metrics              map[string]float64 `yaml:"custom_metrics" db:"config"`
    50  		MetricsStandby       map[string]float64 `yaml:"custom_metrics_standby" db:"config_standby"`
    51  		Kind                 Kind               `yaml:"kind" db:"dbtype"`
    52  		IncludePattern       string             `yaml:"include_pattern" db:"include_pattern"`
    53  		ExcludePattern       string             `yaml:"exclude_pattern" db:"exclude_pattern"`
    54  		PresetMetrics        string             `yaml:"preset_metrics" db:"preset_config"`
    55  		PresetMetricsStandby string             `yaml:"preset_metrics_standby" db:"preset_config_standby"`
    56  		IsEnabled            bool               `yaml:"is_enabled" db:"is_enabled"`
    57  		CustomTags           map[string]string  `yaml:"custom_tags" db:"custom_tags"`
    58  		HostConfig           HostConfigAttrs    `yaml:"host_config" db:"host_config"`
    59  		OnlyIfMaster         bool               `yaml:"only_if_master" db:"only_if_master"`
    60  	}
    61  
    62  	Sources []Source
    63  )
    64  
    65  func (s Source) IsDefaultGroup() bool {
    66  	return s.Group == "" || s.Group == "default"
    67  }
    68  
    69  func (srcs Sources) Validate() (Sources, error) {
    70  	names := map[string]any{}
    71  	for _, src := range srcs {
    72  		if _, ok := names[src.Name]; ok {
    73  			return nil, fmt.Errorf("duplicate source with name '%s' found", src.Name)
    74  		}
    75  		names[src.Name] = nil
    76  		if src.Kind == "" {
    77  			src.Kind = SourcePostgres
    78  		}
    79  	}
    80  	return srcs, nil
    81  }
    82  
    83  func (s *Source) GetDatabaseName() string {
    84  	if с, err := pgx.ParseConfig(s.ConnStr); err == nil {
    85  		return с.Database
    86  	}
    87  	return ""
    88  }
    89  
    90  func (s Source) Equal(s2 Source) bool {
    91  	return s.Name == s2.Name &&
    92  		s.Group == s2.Group &&
    93  		s.ConnStr == s2.ConnStr &&
    94  		s.Kind == s2.Kind &&
    95  		s.IsEnabled == s2.IsEnabled &&
    96  		s.IncludePattern == s2.IncludePattern &&
    97  		s.ExcludePattern == s2.ExcludePattern &&
    98  		(s.PresetMetrics == s2.PresetMetrics || reflect.DeepEqual(s.Metrics, s2.Metrics)) &&
    99  		(s.PresetMetricsStandby == s2.PresetMetricsStandby || reflect.DeepEqual(s.MetricsStandby, s2.MetricsStandby)) &&
   100  		s.OnlyIfMaster == s2.OnlyIfMaster &&
   101  		reflect.DeepEqual(s.CustomTags, s2.CustomTags) &&
   102  		reflect.DeepEqual(s.HostConfig, s2.HostConfig)
   103  }
   104  
   105  func (s *Source) Clone() *Source {
   106  	c := new(Source)
   107  	*c = *s
   108  	c.Metrics = maps.Clone(s.Metrics)
   109  	c.MetricsStandby = maps.Clone(s.MetricsStandby)
   110  	c.CustomTags = maps.Clone(s.CustomTags)
   111  	return c
   112  }
   113  
   114  type HostConfigAttrs struct {
   115  	DcsType                string   `yaml:"dcs_type"`
   116  	DcsEndpoints           []string `yaml:"dcs_endpoints"`
   117  	Scope                  string
   118  	Namespace              string
   119  	Username               string
   120  	Password               string
   121  	CAFile                 string                             `yaml:"ca_file"`
   122  	CertFile               string                             `yaml:"cert_file"`
   123  	KeyFile                string                             `yaml:"key_file"`
   124  	LogsGlobPath           string                             `yaml:"logs_glob_path"`   // default $data_directory / $log_directory / *.csvlog
   125  	LogsMatchRegex         string                             `yaml:"logs_match_regex"` // default is for CSVLOG format. needs to capture following named groups: log_time, user_name, database_name and error_severity
   126  	PerMetricDisabledTimes []HostConfigPerMetricDisabledTimes `yaml:"per_metric_disabled_intervals"`
   127  }
   128  
   129  type HostConfigPerMetricDisabledTimes struct { // metric gathering override per host / metric / time
   130  	Metrics       []string `yaml:"metrics"`
   131  	DisabledTimes []string `yaml:"disabled_times"`
   132  	DisabledDays  string   `yaml:"disabled_days"`
   133  }
   134  
   135  type Reader interface {
   136  	GetSources() (Sources, error)
   137  }
   138  
   139  type Writer interface {
   140  	WriteSources(Sources) error
   141  	DeleteSource(string) error
   142  	UpdateSource(md Source) error
   143  }
   144  
   145  type ReaderWriter interface {
   146  	Reader
   147  	Writer
   148  }
   149