@@ -4,10 +4,34 @@ import (
4
4
"fmt"
5
5
"os"
6
6
"path"
7
+ "regexp"
8
+ "strconv"
9
+ "strings"
7
10
8
11
"gopkg.in/yaml.v2"
9
12
)
10
13
14
+ // Column usage types
15
+ const (
16
+ Discard ColumnUsage = iota // Ignore this column
17
+ Label // Use this column as a label
18
+ Counter // Use this column as a counter
19
+ Gauge // Use this column as a gauge
20
+
21
+ NoVersion PgVersion = - 1
22
+ )
23
+
24
+ var (
25
+ pgVerRegex = regexp .MustCompile (`^(\d+)(?:\.(\d+))?(?:\.(\d+))?$` )
26
+
27
+ columnUsageMapping = map [string ]ColumnUsage {
28
+ "DISCARD" : Discard ,
29
+ "LABEL" : Label ,
30
+ "COUNTER" : Counter ,
31
+ "GAUGE" : Gauge ,
32
+ }
33
+ )
34
+
11
35
// ConfigInterface describes Config methods
12
36
type ConfigInterface interface {
13
37
Load () error
@@ -21,6 +45,189 @@ type Config struct {
21
45
dbs map [string ]DbConfig
22
46
}
23
47
48
+ // ColumnUsage describes column usage
49
+ type ColumnUsage int
50
+
51
+ // PgVersion describes version in server_version_num format
52
+ type PgVersion int
53
+
54
+ // Metrics describe metrics
55
+ type Metrics map [string ]Metric
56
+
57
+ // VerSQLs contain version specific SQLs
58
+ type VerSQLs []VerSQL
59
+
60
+ // Metric describes metric
61
+ type Metric struct {
62
+ Usage ColumnUsage `yaml:"usage"`
63
+ Description string `yaml:"description"`
64
+ }
65
+
66
+ // VerSQL describes PostgreSQL version specific SQL
67
+ type VerSQL struct {
68
+ SQL string
69
+ MinVer PgVersion
70
+ MaxVer PgVersion
71
+ }
72
+
73
+ // Query describes query
74
+ type Query struct {
75
+ Name string
76
+ Metrics Metrics `yaml:"metrics"`
77
+ VerSQL VerSQLs `yaml:"query"`
78
+ NameColumn string `yaml:"nameColumn"`
79
+ ValueColumn string `yaml:"valueColumn"`
80
+ }
81
+
82
+ // UnmarshalYAML unmarshals the yaml
83
+ func (v * VerSQLs ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
84
+ res := make (VerSQLs , 0 )
85
+ var val interface {}
86
+
87
+ err := unmarshal (& val )
88
+ if err != nil {
89
+ return fmt .Errorf ("could not unmarshal: %v" , err )
90
+ }
91
+
92
+ switch val := val .(type ) {
93
+ case map [interface {}]interface {}:
94
+ for k , v := range val {
95
+ minPg , maxPg := parseVersionRange (fmt .Sprintf ("%v" , k ))
96
+ res = append (res , VerSQL {
97
+ MinVer : minPg ,
98
+ MaxVer : maxPg ,
99
+ SQL : v .(string ),
100
+ })
101
+ }
102
+ case interface {}:
103
+ res = append (res , VerSQL {
104
+ SQL : val .(string ),
105
+ })
106
+ }
107
+
108
+ * v = res
109
+
110
+ return nil
111
+ }
112
+
113
+ // UnmarshalYAML unmarshals the yaml
114
+ func (c * ColumnUsage ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
115
+ var value string
116
+ unmarshal (& value )
117
+ cu , ok := columnUsageMapping [value ]
118
+ if ! ok {
119
+ return fmt .Errorf ("unknown usage: %v" , value )
120
+ }
121
+
122
+ * c = cu
123
+
124
+ return nil
125
+ }
126
+
127
+ // UnmarshalYAML unmarshals the yaml
128
+ func (m * Metrics ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
129
+ value := make (map [string ]Metric , 0 )
130
+ queryMetrics := make ([]map [string ]Metric , 0 )
131
+
132
+ if err := unmarshal (& queryMetrics ); err != nil {
133
+ return err
134
+ }
135
+
136
+ for _ , metrics := range queryMetrics {
137
+ for name , descr := range metrics {
138
+ value [name ] = descr
139
+ }
140
+ }
141
+
142
+ * m = value
143
+
144
+ return nil
145
+ }
146
+
147
+ // UnmarshalYAML unmarshals the yaml
148
+ func (v * PgVersion ) UnmarshalYAML (unmarshal func (interface {}) error ) error {
149
+ var str string
150
+ if err := unmarshal (& str ); err != nil {
151
+ return err
152
+ }
153
+
154
+ val := strings .Replace (str , "." , "" , - 1 )
155
+ intVal , err := strconv .Atoi (val )
156
+ if err != nil {
157
+ return fmt .Errorf ("could not convert string: %v" , err )
158
+ }
159
+ * v = PgVersion (intVal )
160
+ return nil
161
+ }
162
+
163
+ // PgVersion returns string representation of the version
164
+ func (v PgVersion ) String () string {
165
+ return fmt .Sprintf ("%d.%d.%d" , v / 10000 , (v / 100 )% 100 , v % 100 )
166
+ }
167
+
168
+ // Query returns query for the requested postgresql version
169
+ func (v VerSQLs ) Query (version PgVersion ) string {
170
+ if version == NoVersion ||
171
+ (len (v ) == 1 && v [0 ].MaxVer == PgVersion (0 ) && v [0 ].MinVer == PgVersion (0 )) {
172
+ return v [0 ].SQL
173
+ }
174
+
175
+ for _ , query := range v {
176
+ if (version >= query .MinVer || query .MinVer == 0 ) && (version < query .MaxVer || query .MaxVer == 0 ) {
177
+ return query .SQL
178
+ }
179
+ }
180
+
181
+ return ""
182
+ }
183
+
184
+ func parseVersion (str string ) PgVersion {
185
+ var res int
186
+ matches := pgVerRegex .FindStringSubmatch (str )
187
+ if matches == nil {
188
+ return PgVersion (res )
189
+ }
190
+ if matches [1 ] != "" {
191
+ val , _ := strconv .Atoi (matches [1 ])
192
+ res = val * 10000
193
+ if val > 9 && matches [2 ] != "" {
194
+ val , _ := strconv .Atoi (matches [2 ])
195
+ res += val
196
+ } else if matches [2 ] != "" {
197
+ val , _ := strconv .Atoi (matches [2 ])
198
+ res += val * 100
199
+ if matches [3 ] != "" {
200
+ val , _ := strconv .Atoi (matches [3 ])
201
+ res += val
202
+ }
203
+ }
204
+ }
205
+
206
+ return PgVersion (res )
207
+ }
208
+
209
+ func parseVersionRange (str string ) (PgVersion , PgVersion ) {
210
+ var min , max PgVersion
211
+ if str == "" {
212
+ return min , max
213
+ }
214
+
215
+ parts := strings .Split (str , "-" )
216
+ if len (parts ) == 1 {
217
+ min = parseVersion (parts [0 ])
218
+ max = min
219
+ } else {
220
+ if parts [0 ] != "" {
221
+ min = parseVersion (parts [0 ])
222
+ }
223
+ if parts [1 ] != "" {
224
+ max = parseVersion (parts [1 ])
225
+ }
226
+ }
227
+
228
+ return min , max
229
+ }
230
+
24
231
// New creates new config
25
232
func New (filename string ) * Config {
26
233
cfg := Config {
0 commit comments