k3s/vendor/github.com/rancher/kine/pkg/drivers/pgsql/pgsql.go

166 lines
3.5 KiB
Go
Raw Normal View History

2019-05-15 23:05:32 +00:00
package pgsql
import (
2019-08-22 05:12:46 +00:00
"context"
2019-05-15 23:05:32 +00:00
"database/sql"
2019-06-15 19:18:08 +00:00
"net/url"
2019-05-15 23:05:32 +00:00
"regexp"
"strconv"
"strings"
"github.com/lib/pq"
2019-08-22 05:12:46 +00:00
"github.com/rancher/kine/pkg/drivers/generic"
"github.com/rancher/kine/pkg/logstructured"
"github.com/rancher/kine/pkg/logstructured/sqllog"
"github.com/rancher/kine/pkg/server"
"github.com/rancher/kine/pkg/tls"
2019-05-15 23:05:32 +00:00
)
2019-06-15 19:18:08 +00:00
const (
defaultDSN = "postgres://postgres:postgres@localhost/"
)
2019-05-15 23:05:32 +00:00
var (
schema = []string{
2019-08-22 05:12:46 +00:00
`create table if not exists kine
2019-05-15 23:05:32 +00:00
(
2019-08-22 05:12:46 +00:00
id SERIAL PRIMARY KEY,
2019-05-15 23:05:32 +00:00
name TEXT,
2019-08-22 05:12:46 +00:00
created INTEGER,
deleted INTEGER,
2019-05-15 23:05:32 +00:00
create_revision INTEGER,
2019-08-22 05:12:46 +00:00
prev_revision INTEGER,
lease INTEGER,
value bytea,
old_value bytea
2019-05-15 23:05:32 +00:00
);`,
2019-08-22 05:12:46 +00:00
`CREATE INDEX IF NOT EXISTS kine_name_index ON kine (name)`,
`CREATE UNIQUE INDEX IF NOT EXISTS kine_name_prev_revision_uindex ON kine (name, prev_revision)`,
2019-05-15 23:05:32 +00:00
}
2019-06-15 19:18:08 +00:00
createDB = "create database "
2019-05-15 23:05:32 +00:00
)
2019-08-22 05:12:46 +00:00
func New(dataSourceName string, tlsInfo tls.Config) (server.Backend, error) {
2019-06-15 19:18:08 +00:00
parsedDSN, err := prepareDSN(dataSourceName, tlsInfo)
if err != nil {
return nil, err
2019-05-15 23:05:32 +00:00
}
2019-08-22 05:12:46 +00:00
2019-06-15 19:18:08 +00:00
if err := createDBIfNotExist(parsedDSN); err != nil {
return nil, err
2019-05-15 23:05:32 +00:00
}
2019-08-22 05:12:46 +00:00
dialect, err := generic.Open("postgres", parsedDSN, "$", true)
2019-05-15 23:05:32 +00:00
if err != nil {
return nil, err
}
2019-08-22 05:12:46 +00:00
if err := setup(dialect.DB); err != nil {
return nil, err
}
dialect.Migrate(context.Background())
return logstructured.New(sqllog.New(dialect)), nil
}
func setup(db *sql.DB) error {
2019-05-15 23:05:32 +00:00
for _, stmt := range schema {
_, err := db.Exec(stmt)
if err != nil {
2019-08-22 05:12:46 +00:00
return err
2019-05-15 23:05:32 +00:00
}
}
2019-08-22 05:12:46 +00:00
return nil
2019-05-15 23:05:32 +00:00
}
func createDBIfNotExist(dataSourceName string) error {
2019-06-15 19:18:08 +00:00
u, err := url.Parse(dataSourceName)
if err != nil {
return err
}
2019-08-22 05:12:46 +00:00
2019-06-15 19:18:08 +00:00
dbName := strings.SplitN(u.Path, "/", 2)[1]
2019-05-15 23:05:32 +00:00
db, err := sql.Open("postgres", dataSourceName)
if err != nil {
return err
}
2019-08-22 05:12:46 +00:00
defer db.Close()
2019-06-15 19:18:08 +00:00
err = db.Ping()
2019-05-15 23:05:32 +00:00
// check if database already exists
2019-06-15 19:18:08 +00:00
if _, ok := err.(*pq.Error); !ok {
2019-05-15 23:05:32 +00:00
return err
}
2019-06-15 19:18:08 +00:00
if err := err.(*pq.Error); err.Code != "42P04" {
if err.Code != "3D000" {
return err
}
// database doesn't exit, will try to create it
u.Path = "/postgres"
db, err := sql.Open("postgres", u.String())
if err != nil {
return err
}
2019-08-22 05:12:46 +00:00
defer db.Close()
2019-06-15 19:18:08 +00:00
_, err = db.Exec(createDB + dbName + ";")
if err != nil {
return err
}
}
2019-05-15 23:05:32 +00:00
return nil
}
func q(sql string) string {
regex := regexp.MustCompile(`\?`)
pref := "$"
n := 0
return regex.ReplaceAllStringFunc(sql, func(string) string {
n++
return pref + strconv.Itoa(n)
})
}
2019-06-15 19:18:08 +00:00
2019-08-22 05:12:46 +00:00
func prepareDSN(dataSourceName string, tlsInfo tls.Config) (string, error) {
2019-06-15 19:18:08 +00:00
if len(dataSourceName) == 0 {
dataSourceName = defaultDSN
} else {
dataSourceName = "postgres://" + dataSourceName
}
u, err := url.Parse(dataSourceName)
if err != nil {
return "", err
}
if len(u.Path) == 0 || u.Path == "/" {
u.Path = "/kubernetes"
}
queryMap, err := url.ParseQuery(u.RawQuery)
if err != nil {
return "", err
}
// set up tls dsn
params := url.Values{}
2019-08-22 05:12:46 +00:00
sslmode := ""
2019-06-15 19:18:08 +00:00
if _, ok := queryMap["sslcert"]; tlsInfo.CertFile != "" && !ok {
params.Add("sslcert", tlsInfo.CertFile)
sslmode = "verify-full"
}
if _, ok := queryMap["sslkey"]; tlsInfo.KeyFile != "" && !ok {
params.Add("sslkey", tlsInfo.KeyFile)
sslmode = "verify-full"
}
if _, ok := queryMap["sslrootcert"]; tlsInfo.CAFile != "" && !ok {
params.Add("sslrootcert", tlsInfo.CAFile)
sslmode = "verify-full"
}
2019-08-22 05:12:46 +00:00
if _, ok := queryMap["sslmode"]; !ok && sslmode != "" {
2019-06-15 19:18:08 +00:00
params.Add("sslmode", sslmode)
}
for k, v := range queryMap {
params.Add(k, v[0])
}
u.RawQuery = params.Encode()
return u.String(), nil
}