forked from metal-stack/go-ipam
-
Notifications
You must be signed in to change notification settings - Fork 0
/
postgres.go
93 lines (80 loc) · 2.89 KB
/
postgres.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package ipam
import (
"fmt"
"net/url"
"sync"
"github.com/jmoiron/sqlx"
// import for sqlx to use postgres driver
_ "github.com/lib/pq"
)
const postgresSchema = `
CREATE TABLE IF NOT EXISTS prefixes (
cidr text PRIMARY KEY NOT NULL,
prefix JSONB
);
CREATE INDEX IF NOT EXISTS prefix_idx ON prefixes USING GIN(prefix);
`
// SSLMode specifies how to configure ssl encryption to the database
type SSLMode string
func (s SSLMode) String() string {
return "sslmode=" + string(s)
}
const (
// SSLModeAllow I don't care about security
// but I will pay the overhead of encryption if the server insists on it
SSLModeAllow = SSLMode("allow")
// SSLModeDisable I don't care about security
// and I don't want to pay the overhead of encryption.
SSLModeDisable = SSLMode("disable")
// SSLModePrefer I don't care about encryption
// but I wish to pay the overhead of encryption if the server supports it.
SSLModePrefer = SSLMode("prefer")
// SSLModeRequire I want my data to be encrypted and I accept the overhead.
// I trust that the network will make sure I always connect to the server I want.
SSLModeRequire = SSLMode("require")
// SSLModeVerifyCA I want my data encrypted and I accept the overhead.
// I want to be sure that I connect to a server that I trust.
SSLModeVerifyCA = SSLMode("verify-ca")
// SSLModeVerifyFull I want my data encrypted and I accept the overhead.
// I want to be sure that I connect to a server I trust, and that it's the one I specify.
SSLModeVerifyFull = SSLMode("verify-full")
)
// NewPostgresStorage creates a new Storage which uses postgres.
func NewPostgresStorage(host, port, user, password, dbname string, sslmode SSLMode) (Storage, error) {
return newPostgres(host, port, user, password, dbname, sslmode)
}
func newPostgres(host, port, user, password, dbname string, sslmode SSLMode) (*sql, error) {
ds, err := dataSource(host, port, user, password, dbname, sslmode)
if err != nil {
// Already wrapped.
return nil, err
}
db, err := sqlx.Connect("postgres", ds)
if err != nil {
return nil, fmt.Errorf("unable to connect to database:%w", err)
}
_, err = db.Exec(postgresSchema)
if err != nil {
return nil, fmt.Errorf("error creating tables: %w", err)
}
var maxIdLength int
err = db.Get(&maxIdLength, "show max_identifier_length")
if err != nil {
return nil, fmt.Errorf("failed to get max_identifier_length: %w", err)
}
sql := &sql{
db: db,
maxIdLength: maxIdLength,
tables: sync.Map{},
}
sql.tables.Store(defaultNamespace, struct{}{})
return sql, nil
}
func dataSource(host, port, user, password, dbname string, sslmode SSLMode) (string, error) {
baseURL := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?%s", url.PathEscape(user), url.PathEscape(password), host, port, dbname, sslmode)
parsedURL, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("%w: unable to parse base URL:%s", err, baseURL)
}
return parsedURL.String(), nil
}