k3s/vendor/github.com/k3s-io/kine/pkg/drivers/mysql/mysql.go

179 lines
4.2 KiB
Go

package mysql
import (
"context"
cryptotls "crypto/tls"
"database/sql"
"github.com/go-sql-driver/mysql"
"github.com/k3s-io/kine/pkg/drivers/generic"
"github.com/k3s-io/kine/pkg/logstructured"
"github.com/k3s-io/kine/pkg/logstructured/sqllog"
"github.com/k3s-io/kine/pkg/server"
"github.com/k3s-io/kine/pkg/tls"
"github.com/sirupsen/logrus"
)
const (
defaultUnixDSN = "root@unix(/var/run/mysqld/mysqld.sock)/"
defaultHostDSN = "root@tcp(127.0.0.1)/"
)
var (
schema = []string{
`CREATE TABLE IF NOT EXISTS kine
(
id INTEGER AUTO_INCREMENT,
name VARCHAR(630),
created INTEGER,
deleted INTEGER,
create_revision INTEGER,
prev_revision INTEGER,
lease INTEGER,
value MEDIUMBLOB,
old_value MEDIUMBLOB,
PRIMARY KEY (id)
);`,
`CREATE INDEX kine_name_index ON kine (name)`,
`CREATE INDEX kine_name_id_index ON kine (name,id)`,
`CREATE INDEX kine_id_deleted_index ON kine (id,deleted)`,
`CREATE INDEX kine_prev_revision_index ON kine (prev_revision)`,
`CREATE UNIQUE INDEX kine_name_prev_revision_uindex ON kine (name, prev_revision)`,
}
createDB = "CREATE DATABASE IF NOT EXISTS "
)
func New(ctx context.Context, dataSourceName string, tlsInfo tls.Config, connPoolConfig generic.ConnectionPoolConfig) (server.Backend, error) {
tlsConfig, err := tlsInfo.ClientConfig()
if err != nil {
return nil, err
}
if tlsConfig != nil {
tlsConfig.MinVersion = cryptotls.VersionTLS11
}
parsedDSN, err := prepareDSN(dataSourceName, tlsConfig)
if err != nil {
return nil, err
}
if err := createDBIfNotExist(parsedDSN); err != nil {
return nil, err
}
dialect, err := generic.Open(ctx, "mysql", parsedDSN, connPoolConfig, "?", false)
if err != nil {
return nil, err
}
dialect.LastInsertID = true
dialect.GetSizeSQL = `
SELECT SUM(data_length + index_length)
FROM information_schema.TABLES
WHERE table_schema = DATABASE() AND table_name = 'kine'`
dialect.CompactSQL = `
DELETE kv FROM kine AS kv
INNER JOIN (
SELECT kp.prev_revision AS id
FROM kine AS kp
WHERE
kp.name != 'compact_rev_key' AND
kp.prev_revision != 0 AND
kp.id <= ?
UNION
SELECT kd.id AS id
FROM kine AS kd
WHERE
kd.deleted != 0 AND
kd.id <= ?
) AS ks
ON kv.id = ks.id`
dialect.TranslateErr = func(err error) error {
if err, ok := err.(*mysql.MySQLError); ok && err.Number == 1062 {
return server.ErrKeyExists
}
return err
}
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 {
logrus.Infof("Configuring database table schema and indexes, this may take a moment...")
for _, stmt := range schema {
logrus.Tracef("SETUP EXEC : %v", generic.Stripped(stmt))
_, err := db.Exec(stmt)
if err != nil {
if mysqlError, ok := err.(*mysql.MySQLError); !ok || mysqlError.Number != 1061 {
return err
}
}
}
logrus.Infof("Database tables and indexes are up to date")
return nil
}
func createDBIfNotExist(dataSourceName string) error {
config, err := mysql.ParseDSN(dataSourceName)
if err != nil {
return err
}
dbName := config.DBName
db, err := sql.Open("mysql", dataSourceName)
if err != nil {
return err
}
_, err = db.Exec(createDB + dbName)
if err != nil {
if mysqlError, ok := err.(*mysql.MySQLError); !ok || mysqlError.Number != 1049 {
return err
}
config.DBName = ""
db, err = sql.Open("mysql", config.FormatDSN())
if err != nil {
return err
}
_, err = db.Exec(createDB + dbName)
if err != nil {
return err
}
}
return nil
}
func prepareDSN(dataSourceName string, tlsConfig *cryptotls.Config) (string, error) {
if len(dataSourceName) == 0 {
dataSourceName = defaultUnixDSN
if tlsConfig != nil {
dataSourceName = defaultHostDSN
}
}
config, err := mysql.ParseDSN(dataSourceName)
if err != nil {
return "", err
}
// setting up tlsConfig
if tlsConfig != nil {
if err := mysql.RegisterTLSConfig("kine", tlsConfig); err != nil {
return "", err
}
config.TLSConfig = "kine"
}
dbName := "kubernetes"
if len(config.DBName) > 0 {
dbName = config.DBName
}
config.DBName = dbName
parsedDSN := config.FormatDSN()
return parsedDSN, nil
}