Migration script added

This commit is contained in:
Dmitry Anderson 2024-11-12 11:08:08 +01:00
parent ddd9ee5f34
commit 72c5d83108
21 changed files with 162 additions and 22 deletions

View File

@ -1,4 +1,4 @@
package common_utils
package commonUtils
import (
"context"

View File

@ -1,4 +1,4 @@
package common_utils
package commonUtils
import (
"strconv"

View File

@ -1,4 +1,4 @@
package common_utils
package commonUtils
func DefaultIfEqual[T comparable](in T, checkEqualTo T, defaultVal T) T {
if in == checkEqualTo {

View File

@ -1,4 +1,4 @@
package common_utils
package commonUtils
import "context"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import (
"sync"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import "sync/atomic"

View File

@ -1,4 +1,4 @@
/*
Provides useful data structures
*/
package ds_utils
package dbUtils

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import (
"context"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import (
"sync"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import "sync"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import (
"sync"

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
// A combination of worker pool and a queue

View File

@ -1,4 +1,4 @@
package ds_utils
package dbUtils
import (
"context"

View File

@ -1,4 +1,4 @@
package ds_utils_test
package dbUtils_test
import (
"context"

View File

@ -1 +1,127 @@
package db_utils
import (
"database/sql"
"errors"
"fmt"
"github.com/golang-migrate/migrate/v4"
pgx "github.com/golang-migrate/migrate/v4/database/pgx/v5"
"github.com/golang-migrate/migrate/v4/database/sqlite3"
_ "github.com/golang-migrate/migrate/v4/source/file"
)
var (
OnNewInstance = errors.New("creating new instance")
OnVersionCheck = errors.New("checking DB version")
OnDrop = errors.New("dropping DB")
)
type MigrationConfig[InstanceCfgT any] struct {
// File path
// format: path or /absolutepath
MigrationsPath string
// format: sqlite3://path/to/db
// postgresql://user:password@ip:port/dbname?conn_opts
DB *sql.DB
// Put -1 for no limit, 0 to down database
// and any number > 0 to limit version
VersionLimit int
// Drop DB before applying migrations
Drop bool
// Config used for migration connects
DriverCfg InstanceCfgT
}
// NewMigrateSQLiteInstance is a newInstance function for sqlite3 database driver
func NewMigrateSQLiteInstance(db *sql.DB, sourceURL string, cfg *sqlite3.Config) (*migrate.Migrate, error) {
driver, err := sqlite3.WithInstance(db, cfg)
if err != nil {
return nil, err
}
return migrate.NewWithDatabaseInstance(sourceURL, "sqlite3", driver)
}
// NewMigratePgxInstance is a newInstance function for pgx/v5 database driver
func NewMigratePgxInstance(db *sql.DB, sourceURL string, cfg *pgx.Config) (*migrate.Migrate, error) {
driver, err := pgx.WithInstance(db, cfg)
if err != nil {
return nil, err
}
return migrate.NewWithDatabaseInstance(sourceURL, "pgx/v5", driver)
}
// DoMigrate applies migration to a database
func DoMigrate[InstanceCfgT any](cfg MigrationConfig[InstanceCfgT],
newInstance func(db *sql.DB, sourceURL string, cfg InstanceCfgT) (*migrate.Migrate, error)) (uint, bool, error) {
var ver uint
var dirty bool
var sourceURL string = fmt.Sprintf("file://%s", cfg.MigrationsPath)
m, err := newInstance(cfg.DB, sourceURL, cfg.DriverCfg)
if err != nil {
return 0, false, errors.Join(OnNewInstance, err)
}
// Drop db if needed, get current db
if cfg.Drop {
if err = m.Drop(); err != nil {
return 0, false, errors.Join(OnDrop, err)
}
// After drop, we have to create new migrate instance
m, err = newInstance(cfg.DB, sourceURL, cfg.DriverCfg)
if err != nil {
return 0, false, errors.Join(OnNewInstance, err)
}
} else {
// It's strange to check DB version after we drop it
// So I put version checking into else statement
ver, dirty, err = m.Version()
if err != nil && !errors.Is(err, migrate.ErrNilVersion) {
return 0, false, errors.Join(OnVersionCheck, err)
}
if dirty {
if err = m.Drop(); err != nil {
return ver, dirty, errors.Join(OnDrop, err)
}
// After drop, we have to create new migrate instance
m, err = newInstance(cfg.DB, sourceURL, cfg.DriverCfg)
if err != nil {
return ver, dirty, errors.Join(OnNewInstance, err)
}
// As we dropped DB
ver = 0
dirty = false
}
}
if cfg.VersionLimit == int(ver) {
return ver, dirty, nil
}
migratingUp := true
if cfg.VersionLimit > 0 {
if cfg.VersionLimit < int(ver) {
migratingUp = false
}
err = m.Migrate(uint(cfg.VersionLimit))
} else if cfg.VersionLimit == 0 {
migratingUp = false
err = m.Down()
} else {
err = m.Up()
}
if err != nil {
if !errors.Is(err, migrate.ErrNoChange) {
return 0, false, errors.Join(OnNewInstance, err,
fmt.Errorf("version limit: %d; migrating up: %v", cfg.VersionLimit, migratingUp))
}
return ver, dirty, nil
}
ver, dirty, err = m.Version()
if err != nil {
return ver, dirty, errors.Join(OnVersionCheck, err)
}
return ver, dirty, nil
}

4
go.mod
View File

@ -3,9 +3,13 @@ module git.mic.pp.ua/anderson/nettools
go 1.23.1
require (
github.com/golang-migrate/migrate/v4 v4.18.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/text v0.18.0 // indirect
)

10
go.sum
View File

@ -1,4 +1,12 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y=
github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
@ -9,6 +17,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=

View File

@ -1,4 +1,4 @@
package net_utils
package netUtils
import (
"bufio"

View File

@ -1,4 +1,4 @@
package net_utils
package netUtils
import (
"encoding/binary"
@ -7,7 +7,7 @@ import (
"net"
"net/http"
common_utils "git.mic.pp.ua/anderson/nettools/common"
common "git.mic.pp.ua/anderson/nettools/common"
)
var (
@ -42,7 +42,7 @@ const (
PRESERVED_IP_RANGE_10 uint32 = IP_10_0_0_0 & MASK8
)
func MaskToBytes[T common_utils.Int](mask T) [4]uint8 {
func MaskToBytes[T common.Int](mask T) [4]uint8 {
if mask > 32 {
panic("mask is invalid: value out of range")
}
@ -75,13 +75,13 @@ func BytesToUint32(in []uint8) (uint32, error) {
type NetInterfaceNamesT = map[string]struct{}
// Those are ones I have on my machine, so they are default, lol
// DefaultNetInterfaceNames are interface names that I have on my machine
var DefaultNetInterfaceNames = NetInterfaceNamesT{
"eth0": {},
"wlan0": {},
}
// Converts IP which is a byte array to an integer
// IpToInt converts IP which is a byte array to an integer
// So it can be used with the bitmasks or be compared fast
func IpToInt(ip net.IP) uint32 {
if len(ip) == 16 {

View File

@ -1,4 +1,4 @@
package net_utils
package netUtils
import (
common_utils "git.mic.pp.ua/anderson/nettools/common"

View File

@ -1,4 +1,4 @@
package net_utils
package netUtils
import "encoding/json"