-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
What Adds the migrate up/down command. Why For applying the database migration. This is a required component for the CI/CD setup.
- Loading branch information
1 parent
0c2d8f2
commit ef6aeda
Showing
10 changed files
with
335 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strconv" | ||
|
||
migrate "github.com/rubenv/sql-migrate" | ||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/support/config" | ||
"github.com/stellar/go/support/log" | ||
"github.com/stellar/wallet-backend/cmd/utils" | ||
"github.com/stellar/wallet-backend/internal/db" | ||
) | ||
|
||
type migrateCmd struct{} | ||
|
||
func (c *migrateCmd) Command() *cobra.Command { | ||
var databaseURL string | ||
cfgOpts := config.ConfigOptions{ | ||
utils.DatabaseURLOption(&databaseURL), | ||
} | ||
|
||
migrateCmd := &cobra.Command{ | ||
Use: "migrate", | ||
Short: "Schema migration helpers", | ||
PersistentPreRunE: utils.DefaultPersistentPreRunE(cfgOpts), | ||
} | ||
|
||
migrateUpCmd := &cobra.Command{ | ||
Use: "up", | ||
Short: "Migrates database up [count] migrations", | ||
Args: cobra.MaximumNArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return c.RunMigrateUp(cmd.Context(), databaseURL, args) | ||
}, | ||
} | ||
|
||
migrateDownCmd := &cobra.Command{ | ||
Use: "down [count]", | ||
Short: "Migrates database down [count] migrations", | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return c.RunMigrateDown(cmd.Context(), databaseURL, args) | ||
}, | ||
} | ||
|
||
migrateCmd.AddCommand(migrateUpCmd) | ||
migrateCmd.AddCommand(migrateDownCmd) | ||
|
||
if err := cfgOpts.Init(migrateCmd); err != nil { | ||
log.Fatalf("Error initializing a config option: %s", err.Error()) | ||
} | ||
|
||
return migrateCmd | ||
} | ||
|
||
func (c *migrateCmd) RunMigrateUp(ctx context.Context, databaseURL string, args []string) error { | ||
var count int | ||
if len(args) > 0 { | ||
var err error | ||
count, err = strconv.Atoi(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("invalid [count] argument: %s", args[0]) | ||
} | ||
} | ||
if err := executeMigrations(ctx, databaseURL, migrate.Up, count); err != nil { | ||
return fmt.Errorf("executing migrate up: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func (c *migrateCmd) RunMigrateDown(ctx context.Context, databaseURL string, args []string) error { | ||
count, err := strconv.Atoi(args[0]) | ||
if err != nil { | ||
return fmt.Errorf("invalid [count] argument: %s", args[0]) | ||
} | ||
if err := executeMigrations(ctx, databaseURL, migrate.Down, count); err != nil { | ||
return fmt.Errorf("executing migrate down: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
func executeMigrations(ctx context.Context, databaseURL string, direction migrate.MigrationDirection, count int) error { | ||
numMigrationsRun, err := db.Migrate(ctx, databaseURL, direction, count) | ||
if err != nil { | ||
return fmt.Errorf("migrating database: %w", err) | ||
} | ||
|
||
if numMigrationsRun == 0 { | ||
log.Ctx(ctx).Info("No migrations applied.") | ||
} else { | ||
log.Ctx(ctx).Infof("Successfully applied %d migrations %s.", numMigrationsRun, migrationDirectionStr(direction)) | ||
} | ||
return nil | ||
} | ||
|
||
func migrationDirectionStr(direction migrate.MigrationDirection) string { | ||
if direction == migrate.Up { | ||
return "up" | ||
} | ||
return "down" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package utils | ||
|
||
import ( | ||
"go/types" | ||
|
||
"github.com/sirupsen/logrus" | ||
"github.com/stellar/go/network" | ||
"github.com/stellar/go/support/config" | ||
) | ||
|
||
func DatabaseURLOption(configKey *string) *config.ConfigOption { | ||
return &config.ConfigOption{ | ||
Name: "database-url", | ||
Usage: "Database connection URL.", | ||
OptType: types.String, | ||
ConfigKey: configKey, | ||
FlagDefault: "postgres://postgres@localhost:5432/wallet-backend?sslmode=disable", | ||
Required: true, | ||
} | ||
} | ||
|
||
func LogLevelOption(configKey *logrus.Level) *config.ConfigOption { | ||
return &config.ConfigOption{ | ||
Name: "log-level", | ||
Usage: `The log level used in this project. Options: "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", or "PANIC".`, | ||
OptType: types.String, | ||
FlagDefault: "TRACE", | ||
ConfigKey: configKey, | ||
CustomSetValue: SetConfigOptionLogLevel, | ||
Required: false, | ||
} | ||
} | ||
|
||
func NetworkPassphraseOption(configKey *string) *config.ConfigOption { | ||
return &config.ConfigOption{ | ||
Name: "network-passphrase", | ||
Usage: "Stellar Network Passphrase to connect.", | ||
OptType: types.String, | ||
ConfigKey: configKey, | ||
FlagDefault: network.TestNetworkPassphrase, | ||
Required: true, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package utils | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/support/config" | ||
) | ||
|
||
func DefaultPersistentPreRunE(cfgOpts config.ConfigOptions) func(_ *cobra.Command, _ []string) error { | ||
return func(_ *cobra.Command, _ []string) error { | ||
if err := cfgOpts.RequireE(); err != nil { | ||
return fmt.Errorf("requiring values of config options: %w", err) | ||
} | ||
if err := cfgOpts.SetValues(); err != nil { | ||
return fmt.Errorf("setting values of config options: %w", err) | ||
} | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package db | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
migrate "github.com/rubenv/sql-migrate" | ||
"github.com/stellar/wallet-backend/internal/db/migrations" | ||
) | ||
|
||
func Migrate(ctx context.Context, databaseURL string, direction migrate.MigrationDirection, count int) (int, error) { | ||
dbConnectionPool, err := OpenDBConnectionPool(databaseURL) | ||
if err != nil { | ||
return 0, fmt.Errorf("connecting to the database: %w", err) | ||
} | ||
defer dbConnectionPool.Close() | ||
|
||
m := migrate.HttpFileSystemMigrationSource{FileSystem: http.FS(migrations.FS)} | ||
db, err := dbConnectionPool.SqlDB(ctx) | ||
if err != nil { | ||
return 0, fmt.Errorf("fetching sql.DB: %w", err) | ||
} | ||
return migrate.ExecMax(db, dbConnectionPool.DriverName(), m, direction, count) | ||
} |
Oops, something went wrong.