pangolin_exporter

Форк
0
238 строк · 5.5 Кб
1
// Copyright 2022 The Prometheus Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package config
15

16
import (
17
	"fmt"
18
	"net/url"
19
	"regexp"
20
	"strings"
21
	"unicode"
22
)
23

24
// DSN represents a parsed datasource. It contains fields for the individual connection components.
25
type DSN struct {
26
	scheme   string
27
	username string
28
	password string
29
	host     string
30
	path     string
31
	query    url.Values
32
}
33

34
// String makes a dsn safe to print by excluding any passwords. This allows dsn to be used in
35
// strings and log messages without needing to call a redaction function first.
36
func (d DSN) String() string {
37
	if d.password != "" {
38
		return fmt.Sprintf("%s://%s:******@%s%s?%s", d.scheme, d.username, d.host, d.path, d.query.Encode())
39
	}
40

41
	if d.username != "" {
42
		return fmt.Sprintf("%s://%s@%s%s?%s", d.scheme, d.username, d.host, d.path, d.query.Encode())
43
	}
44

45
	return fmt.Sprintf("%s://%s%s?%s", d.scheme, d.host, d.path, d.query.Encode())
46
}
47

48
// GetConnectionString returns the URL to pass to the driver for database connections. This value should not be logged.
49
func (d DSN) GetConnectionString() string {
50
	u := url.URL{
51
		Scheme:   d.scheme,
52
		Host:     d.host,
53
		Path:     d.path,
54
		RawQuery: d.query.Encode(),
55
	}
56

57
	// Username and Password
58
	if d.username != "" {
59
		u.User = url.UserPassword(d.username, d.password)
60
	}
61

62
	return u.String()
63
}
64

65
// dsnFromString parses a connection string into a dsn. It will attempt to parse the string as
66
// a URL and as a set of key=value pairs. If both attempts fail, dsnFromString will return an error.
67
func dsnFromString(in string) (DSN, error) {
68
	if strings.HasPrefix(in, "postgresql://") || strings.HasPrefix(in, "postgres://") {
69
		return dsnFromURL(in)
70
	}
71

72
	// Try to parse as key=value pairs
73
	d, err := dsnFromKeyValue(in)
74
	if err == nil {
75
		return d, nil
76
	}
77

78
	// Parse the string as a URL, with the scheme prefixed
79
	d, err = dsnFromURL(fmt.Sprintf("postgresql://%s", in))
80
	if err == nil {
81
		return d, nil
82
	}
83

84
	return DSN{}, fmt.Errorf("could not understand DSN")
85
}
86

87
// dsnFromURL parses the input as a URL and returns the dsn representation.
88
func dsnFromURL(in string) (DSN, error) {
89
	u, err := url.Parse(in)
90
	if err != nil {
91
		return DSN{}, err
92
	}
93
	pass, _ := u.User.Password()
94
	user := u.User.Username()
95

96
	query := u.Query()
97

98
	if queryPass := query.Get("password"); queryPass != "" {
99
		if pass == "" {
100
			pass = queryPass
101
		}
102
	}
103
	query.Del("password")
104

105
	if queryUser := query.Get("user"); queryUser != "" {
106
		if user == "" {
107
			user = queryUser
108
		}
109
	}
110
	query.Del("user")
111

112
	d := DSN{
113
		scheme:   u.Scheme,
114
		username: user,
115
		password: pass,
116
		host:     u.Host,
117
		path:     u.Path,
118
		query:    query,
119
	}
120

121
	return d, nil
122
}
123

124
// dsnFromKeyValue parses the input as a set of key=value pairs and returns the dsn representation.
125
func dsnFromKeyValue(in string) (DSN, error) {
126
	// Attempt to confirm at least one key=value pair before starting the rune parser
127
	connstringRe := regexp.MustCompile(`^ *[a-zA-Z0-9]+ *= *[^= ]+`)
128
	if !connstringRe.MatchString(in) {
129
		return DSN{}, fmt.Errorf("input is not a key-value DSN")
130
	}
131

132
	// Anything other than known fields should be part of the querystring
133
	query := url.Values{}
134

135
	pairs, err := parseKeyValue(in)
136
	if err != nil {
137
		return DSN{}, fmt.Errorf("failed to parse key-value DSN: %v", err)
138
	}
139

140
	// Build the dsn from the key=value pairs
141
	d := DSN{
142
		scheme: "postgresql",
143
	}
144

145
	hostname := ""
146
	port := ""
147

148
	for k, v := range pairs {
149
		switch k {
150
		case "host":
151
			hostname = v
152
		case "port":
153
			port = v
154
		case "user":
155
			d.username = v
156
		case "password":
157
			d.password = v
158
		default:
159
			query.Set(k, v)
160
		}
161
	}
162

163
	if hostname == "" {
164
		hostname = "localhost"
165
	}
166

167
	if port == "" {
168
		d.host = hostname
169
	} else {
170
		d.host = fmt.Sprintf("%s:%s", hostname, port)
171
	}
172

173
	d.query = query
174

175
	return d, nil
176
}
177

178
// parseKeyValue is a key=value parser. It loops over each rune to split out keys and values
179
// and attempting to honor quoted values. parseKeyValue will return an error if it is unable
180
// to properly parse the input.
181
func parseKeyValue(in string) (map[string]string, error) {
182
	out := map[string]string{}
183

184
	inPart := false
185
	inQuote := false
186
	part := []rune{}
187
	key := ""
188
	for _, c := range in {
189
		switch {
190
		case unicode.In(c, unicode.Quotation_Mark):
191
			if inQuote {
192
				inQuote = false
193
			} else {
194
				inQuote = true
195
			}
196
		case unicode.In(c, unicode.White_Space):
197
			if inPart {
198
				if inQuote {
199
					part = append(part, c)
200
				} else {
201
					// Are we finishing a key=value?
202
					if key == "" {
203
						return out, fmt.Errorf("invalid input")
204
					}
205
					out[key] = string(part)
206
					inPart = false
207
					part = []rune{}
208
				}
209
			} else {
210
				// Are we finishing a key=value?
211
				if key == "" {
212
					return out, fmt.Errorf("invalid input")
213
				}
214
				out[key] = string(part)
215
				inPart = false
216
				part = []rune{}
217
				// Do something with the value
218
			}
219
		case c == '=':
220
			if inPart {
221
				inPart = false
222
				key = string(part)
223
				part = []rune{}
224
			} else {
225
				return out, fmt.Errorf("invalid input")
226
			}
227
		default:
228
			inPart = true
229
			part = append(part, c)
230
		}
231
	}
232

233
	if key != "" && len(part) > 0 {
234
		out[key] = string(part)
235
	}
236

237
	return out, nil
238
}
239

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.