From 72c5d831086cb310781164060cfca699354b5769 Mon Sep 17 00:00:00 2001 From: Dmitry Anderson <4nd3r5z0n@gmail.com> Date: Tue, 12 Nov 2024 11:08:08 +0100 Subject: [PATCH] Migration script added --- common/common.go | 2 +- common/convert.go | 2 +- common/defaultVals.go | 2 +- common/types.go | 2 +- data_structures/cbuf.go | 2 +- data_structures/counter.go | 2 +- data_structures/ds.go | 2 +- data_structures/pipe.go | 2 +- data_structures/queue.go | 2 +- data_structures/stack.go | 2 +- data_structures/syncmap.go | 2 +- data_structures/worker_pool.go | 2 +- data_structures/worker_pool_func.go | 2 +- data_structures/worker_pool_test.go | 2 +- db/migrate.go | 126 ++++++++++++++++++++++++++++ go.mod | 4 + go.sum | 10 +++ net/basic_conn.go | 2 +- net/ip.go | 10 +-- net/net.go | 2 +- net/ws.go | 2 +- 21 files changed, 162 insertions(+), 22 deletions(-) diff --git a/common/common.go b/common/common.go index 3baa339..700eb54 100644 --- a/common/common.go +++ b/common/common.go @@ -1,4 +1,4 @@ -package common_utils +package commonUtils import ( "context" diff --git a/common/convert.go b/common/convert.go index 4d3e45d..38ed672 100644 --- a/common/convert.go +++ b/common/convert.go @@ -1,4 +1,4 @@ -package common_utils +package commonUtils import ( "strconv" diff --git a/common/defaultVals.go b/common/defaultVals.go index de947eb..264327b 100644 --- a/common/defaultVals.go +++ b/common/defaultVals.go @@ -1,4 +1,4 @@ -package common_utils +package commonUtils func DefaultIfEqual[T comparable](in T, checkEqualTo T, defaultVal T) T { if in == checkEqualTo { diff --git a/common/types.go b/common/types.go index c9a07b3..cec3b16 100644 --- a/common/types.go +++ b/common/types.go @@ -1,4 +1,4 @@ -package common_utils +package commonUtils import "context" diff --git a/data_structures/cbuf.go b/data_structures/cbuf.go index 8934297..b6b2705 100644 --- a/data_structures/cbuf.go +++ b/data_structures/cbuf.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import ( "sync" diff --git a/data_structures/counter.go b/data_structures/counter.go index 7158773..affeab1 100644 --- a/data_structures/counter.go +++ b/data_structures/counter.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import "sync/atomic" diff --git a/data_structures/ds.go b/data_structures/ds.go index 7362c68..4d82c60 100644 --- a/data_structures/ds.go +++ b/data_structures/ds.go @@ -1,4 +1,4 @@ /* Provides useful data structures */ -package ds_utils +package dbUtils diff --git a/data_structures/pipe.go b/data_structures/pipe.go index 64970bd..d0208d6 100644 --- a/data_structures/pipe.go +++ b/data_structures/pipe.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import ( "context" diff --git a/data_structures/queue.go b/data_structures/queue.go index 40ae674..f568dfc 100644 --- a/data_structures/queue.go +++ b/data_structures/queue.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import ( "sync" diff --git a/data_structures/stack.go b/data_structures/stack.go index e2d6dff..7d9f391 100644 --- a/data_structures/stack.go +++ b/data_structures/stack.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import "sync" diff --git a/data_structures/syncmap.go b/data_structures/syncmap.go index 95271fb..d6bf1d4 100644 --- a/data_structures/syncmap.go +++ b/data_structures/syncmap.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import ( "sync" diff --git a/data_structures/worker_pool.go b/data_structures/worker_pool.go index d0ffd67..1257c45 100644 --- a/data_structures/worker_pool.go +++ b/data_structures/worker_pool.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils // A combination of worker pool and a queue diff --git a/data_structures/worker_pool_func.go b/data_structures/worker_pool_func.go index 0beaa64..3c20b3b 100644 --- a/data_structures/worker_pool_func.go +++ b/data_structures/worker_pool_func.go @@ -1,4 +1,4 @@ -package ds_utils +package dbUtils import ( "context" diff --git a/data_structures/worker_pool_test.go b/data_structures/worker_pool_test.go index 2c42003..f95a514 100644 --- a/data_structures/worker_pool_test.go +++ b/data_structures/worker_pool_test.go @@ -1,4 +1,4 @@ -package ds_utils_test +package dbUtils_test import ( "context" diff --git a/db/migrate.go b/db/migrate.go index 998bc94..218c4c7 100644 --- a/db/migrate.go +++ b/db/migrate.go @@ -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 +} diff --git a/go.mod b/go.mod index 9704584..033dfb2 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index e091b4f..3750adf 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/net/basic_conn.go b/net/basic_conn.go index 522dba6..bf72b02 100644 --- a/net/basic_conn.go +++ b/net/basic_conn.go @@ -1,4 +1,4 @@ -package net_utils +package netUtils import ( "bufio" diff --git a/net/ip.go b/net/ip.go index 5841d84..345c79a 100644 --- a/net/ip.go +++ b/net/ip.go @@ -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 { diff --git a/net/net.go b/net/net.go index 1f2cad4..832ce4f 100644 --- a/net/net.go +++ b/net/net.go @@ -1,4 +1,4 @@ -package net_utils +package netUtils import ( common_utils "git.mic.pp.ua/anderson/nettools/common" diff --git a/net/ws.go b/net/ws.go index a2ffc68..bb4ed71 100644 --- a/net/ws.go +++ b/net/ws.go @@ -1,4 +1,4 @@ -package net_utils +package netUtils import "encoding/json"