From 65d8f883dca3c46e6ececada1eb8dc386374b5db Mon Sep 17 00:00:00 2001 From: lorsan Date: Wed, 15 Apr 2026 19:03:51 +0300 Subject: [PATCH] feat: create migrator --- go.mod | 3 +- go.sum | 6 +++ internal/hub/app/app.go | 24 +++++++++- internal/hub/embed.go | 6 +++ ...20260415151037_create_agent_table.down.sql | 4 ++ .../20260415151037_create_agent_table.up.sql | 14 ++++++ internal/hub/migrator/migrator.go | 45 +++++++++++++++++++ 7 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 internal/hub/embed.go create mode 100644 internal/hub/migrations/20260415151037_create_agent_table.down.sql create mode 100644 internal/hub/migrations/20260415151037_create_agent_table.up.sql create mode 100644 internal/hub/migrator/migrator.go diff --git a/go.mod b/go.mod index 0ea2513..43ffb6b 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( require ( github.com/BurntSushi/toml v1.2.1 // indirect - github.com/Microsoft/go-winio v0.4.21 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect @@ -25,6 +25,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang-migrate/migrate/v4 v4.19.1 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index 3ab14ab..ba537f0 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Microsoft/go-winio v0.4.21 h1:+6mVbXh4wPzUrl1COX9A+ZCvEpYsOBZ6/+kwDnvLyro= github.com/Microsoft/go-winio v0.4.21/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -16,6 +18,7 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= @@ -31,6 +34,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-migrate/migrate/v4 v4.19.1 h1:OCyb44lFuQfYXYLx1SCxPZQGU7mcaZ7gH9yH4jSFbBA= +github.com/golang-migrate/migrate/v4 v4.19.1/go.mod h1:CTcgfjxhaUtsLipnLoQRWCrjYXycRz/g5+RWDuYgPrE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -71,6 +76,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= diff --git a/internal/hub/app/app.go b/internal/hub/app/app.go index 86ab0ef..1349cf4 100644 --- a/internal/hub/app/app.go +++ b/internal/hub/app/app.go @@ -1,10 +1,13 @@ package app import ( + "database/sql" "fmt" standartlog "log" "net" + hubdir "github.com/lorsanstand/HomeOps-Hub/internal/hub" + "github.com/lorsanstand/HomeOps-Hub/internal/hub/migrator" grpcserv "github.com/lorsanstand/HomeOps-Hub/internal/hub/rpc" "github.com/lorsanstand/HomeOps-Hub/internal/hub/service/hub_service" "github.com/lorsanstand/HomeOps-Hub/internal/shared/config" @@ -29,9 +32,28 @@ func NewApp() *App { } func (a *App) Run() { - err := a.hubServe() + migratePGConn, err := sql.Open("pgx", a.cfg.GetURLPostgres()) + if err != nil { + a.log.Error().Err(err).Msg("failed to connect to the database") + return + } + defer migratePGConn.Close() + + mgrt, err := migrator.NewMigrator(hubdir.MigrationsFS, "migrations") + if err != nil { + a.log.Error().Err(err).Msg("failed create migrator") + return + } + + if err = mgrt.ApplyMigrations(migratePGConn); err != nil { + a.log.Error().Err(err).Msg("migrations were not applied") + } + migratePGConn.Close() + + err = a.hubServe() if err != nil { a.log.Error().Err(err).Msg("failed to start the server") + return } } diff --git a/internal/hub/embed.go b/internal/hub/embed.go new file mode 100644 index 0000000..1938b80 --- /dev/null +++ b/internal/hub/embed.go @@ -0,0 +1,6 @@ +package hub + +import "embed" + +//go:embed migrations/*.sql +var MigrationsFS embed.FS diff --git a/internal/hub/migrations/20260415151037_create_agent_table.down.sql b/internal/hub/migrations/20260415151037_create_agent_table.down.sql new file mode 100644 index 0000000..7bd2985 --- /dev/null +++ b/internal/hub/migrations/20260415151037_create_agent_table.down.sql @@ -0,0 +1,4 @@ +DROP INDEX idx_agent_id_id; +DROP INDEX idx_agent_id; + +DROP TABLE agents IF EXISTS agents; \ No newline at end of file diff --git a/internal/hub/migrations/20260415151037_create_agent_table.up.sql b/internal/hub/migrations/20260415151037_create_agent_table.up.sql new file mode 100644 index 0000000..1e7cf7e --- /dev/null +++ b/internal/hub/migrations/20260415151037_create_agent_table.up.sql @@ -0,0 +1,14 @@ +CREATE TABLE agents ( + id BIGINT UNIQUE PRIMARY KEY, + agent_id VARCHAR(32) UNIQUE NOT NULL, + agent_name VARCHAR(255), + architecture VARCHAR(10) NOT NULL, + system VARCHAR(10) NOT NULL, + hostname VARCHAR(100) NOT NULL, + version VARCHAR(10) NOT NULL, + capabilities JSON, + registered_at timestamp without time zone DEFAULT now() NOT NULL +); + +CREATE UNIQUE INDEX idx_agent_id ON agent (id); +CREATE UNIQUE INDEX idx_agent_id_id On agent (agent_id) \ No newline at end of file diff --git a/internal/hub/migrator/migrator.go b/internal/hub/migrator/migrator.go new file mode 100644 index 0000000..39a5830 --- /dev/null +++ b/internal/hub/migrator/migrator.go @@ -0,0 +1,45 @@ +package migrator + +import ( + "database/sql" + "embed" + "errors" + "fmt" + + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/postgres" + "github.com/golang-migrate/migrate/v4/source" + "github.com/golang-migrate/migrate/v4/source/iofs" +) + +type Migrator struct { + srcDriver source.Driver +} + +func NewMigrator(sqlFiles embed.FS, dirname string) (*Migrator, error) { + d, err := iofs.New(sqlFiles, dirname) + if err != nil { + return nil, fmt.Errorf("failed to initialize migration driver: %w", err) + } + return &Migrator{srcDriver: d}, nil +} + +func (m *Migrator) ApplyMigrations(db *sql.DB) error { + driver, err := postgres.WithInstance(db, &postgres.Config{}) + if err != nil { + return fmt.Errorf("unable to create db instance: %w", err) + } + + migrator, err := migrate.NewWithInstance("migration_embeded_sql_files", m.srcDriver, "psql_db", driver) + if err != nil { + return fmt.Errorf("unable to create migration: %w", err) + } + + defer migrator.Close() + + if err = migrator.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) { + return fmt.Errorf("unable to apply migrations: %w", err) + } + + return nil +}