280 lines
6.1 KiB
Go
280 lines
6.1 KiB
Go
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
|
//go:build plus
|
|
|
|
package dbs
|
|
|
|
import (
|
|
"database/sql"
|
|
"github.com/TeaOSLab/EdgeDNS/internal/models"
|
|
"github.com/TeaOSLab/EdgeDNS/internal/remotelogs"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
const sqliteMigrationMarkerKey = "meta:migrated_from_sqlite"
|
|
|
|
func (this *DB) migrateSQLiteIfNeeded() error {
|
|
if len(this.path) == 0 || this.path == this.storePath {
|
|
return nil
|
|
}
|
|
|
|
_, err := os.Stat(this.path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
ok, err := this.hasMigrationMarker()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if ok {
|
|
removeErr := removeSQLiteFiles(this.path)
|
|
if removeErr != nil {
|
|
remotelogs.Warn("DB", "remove sqlite files failed after migration: "+removeErr.Error())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
remotelogs.Println("DB", "migrating sqlite database from '"+this.path+"' to '"+this.storePath+"' ...")
|
|
|
|
err = this.truncateMigratedData()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.importSQLiteData()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.rawDB.Set([]byte(sqliteMigrationMarkerKey), []byte("1"), defaultWriteOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = flushRawDB(this.rawDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
removeErr := removeSQLiteFiles(this.path)
|
|
if removeErr != nil {
|
|
remotelogs.Warn("DB", "remove sqlite files failed after migration: "+removeErr.Error())
|
|
}
|
|
|
|
remotelogs.Println("DB", "migrated sqlite database to pebble")
|
|
return nil
|
|
}
|
|
|
|
func (this *DB) hasMigrationMarker() (bool, error) {
|
|
_, closer, err := this.rawDB.Get([]byte(sqliteMigrationMarkerKey))
|
|
if err != nil {
|
|
if isNotFound(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
_ = closer.Close()
|
|
return true, nil
|
|
}
|
|
|
|
func (this *DB) truncateMigratedData() error {
|
|
for _, prefix := range []string{
|
|
domainPrefix,
|
|
domainClusterIndex,
|
|
recordPrefix,
|
|
routePrefix,
|
|
keyPrefix,
|
|
agentIPPrefix,
|
|
"meta:",
|
|
} {
|
|
err := this.rawDB.DeleteRange([]byte(prefix), prefixUpperBound([]byte(prefix)), defaultWriteOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (this *DB) importSQLiteData() error {
|
|
sqliteDB, err := sql.Open("sqlite3", "file:"+this.path+"?mode=ro")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
_ = sqliteDB.Close()
|
|
}()
|
|
|
|
err = this.importSQLiteDomains(sqliteDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = this.importSQLiteRecords(sqliteDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = this.importSQLiteRoutes(sqliteDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = this.importSQLiteKeys(sqliteDB)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return this.importSQLiteAgentIPs(sqliteDB)
|
|
}
|
|
|
|
func (this *DB) importSQLiteDomains(sqliteDB *sql.DB) error {
|
|
rows, err := sqliteDB.Query(`SELECT "id", "clusterId", "userId", "name", "tsig", "version" FROM "domains_v2" ORDER BY "id" ASC`)
|
|
if err != nil {
|
|
return ignoreMissingTable(err)
|
|
}
|
|
defer func() {
|
|
_ = rows.Close()
|
|
}()
|
|
|
|
for rows.Next() {
|
|
value := &domainValue{}
|
|
err = rows.Scan(&value.Id, &value.ClusterId, &value.UserId, &value.Name, &value.TSIGJSON, &value.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.saveDomain(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func (this *DB) importSQLiteRecords(sqliteDB *sql.DB) error {
|
|
rows, err := sqliteDB.Query(`SELECT "id", "domainId", "name", "type", "value", "mxPriority", "srvPriority", "srvWeight", "srvPort", "caaFlag", "caaTag", "ttl", "weight", "routeIds", "version" FROM "records_v2" ORDER BY "id" ASC`)
|
|
if err != nil {
|
|
return ignoreMissingTable(err)
|
|
}
|
|
defer func() {
|
|
_ = rows.Close()
|
|
}()
|
|
|
|
for rows.Next() {
|
|
value := &recordValue{}
|
|
var routeIDs string
|
|
err = rows.Scan(&value.Id, &value.DomainId, &value.Name, &value.Type, &value.Value, &value.MXPriority, &value.SRVPriority, &value.SRVWeight, &value.SRVPort, &value.CAAFlag, &value.CAATag, &value.TTL, &value.Weight, &routeIDs, &value.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(routeIDs) > 0 {
|
|
value.RouteIds = strings.Split(routeIDs, ",")
|
|
}
|
|
|
|
err = this.saveJSON(recordKey(value.Id), value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func (this *DB) importSQLiteRoutes(sqliteDB *sql.DB) error {
|
|
rows, err := sqliteDB.Query(`SELECT "id", "userId", "ranges", "priority", "order", "version" FROM "routes_v2" ORDER BY "id" ASC`)
|
|
if err != nil {
|
|
return ignoreMissingTable(err)
|
|
}
|
|
defer func() {
|
|
_ = rows.Close()
|
|
}()
|
|
|
|
for rows.Next() {
|
|
value := &routeValue{}
|
|
err = rows.Scan(&value.Id, &value.UserId, &value.RangesJSON, &value.Priority, &value.Order, &value.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.saveJSON(routeKey(value.Id), value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func (this *DB) importSQLiteKeys(sqliteDB *sql.DB) error {
|
|
rows, err := sqliteDB.Query(`SELECT "id", "domainId", "zoneId", "algo", "secret", "secretType", "version" FROM "keys" ORDER BY "id" ASC`)
|
|
if err != nil {
|
|
return ignoreMissingTable(err)
|
|
}
|
|
defer func() {
|
|
_ = rows.Close()
|
|
}()
|
|
|
|
for rows.Next() {
|
|
value := &models.NSKey{}
|
|
err = rows.Scan(&value.Id, &value.DomainId, &value.ZoneId, &value.Algo, &value.Secret, &value.SecretType, &value.Version)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.saveJSON(keyKey(value.Id), value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func (this *DB) importSQLiteAgentIPs(sqliteDB *sql.DB) error {
|
|
rows, err := sqliteDB.Query(`SELECT "id", "ip", "agentCode" FROM "agentIPs" ORDER BY "id" ASC`)
|
|
if err != nil {
|
|
return ignoreMissingTable(err)
|
|
}
|
|
defer func() {
|
|
_ = rows.Close()
|
|
}()
|
|
|
|
for rows.Next() {
|
|
value := &models.AgentIP{}
|
|
err = rows.Scan(&value.Id, &value.IP, &value.AgentCode)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = this.saveJSON(agentIPKey(value.Id), value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func ignoreMissingTable(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
if strings.Contains(err.Error(), "no such table") {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func removeSQLiteFiles(path string) error {
|
|
var lastErr error
|
|
for _, filename := range []string{path, path + "-shm", path + "-wal"} {
|
|
err := os.Remove(filename)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
lastErr = err
|
|
}
|
|
}
|
|
return lastErr
|
|
}
|