Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: migrate command #18

Merged
merged 7 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 14 additions & 38 deletions cmd/ingest.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package cmd

import (
"fmt"
"go/types"

_ "github.com/lib/pq"
"github.com/spf13/cobra"
"github.com/stellar/go/network"
"github.com/stellar/go/support/config"
"github.com/stellar/go/support/log"
"github.com/stellar/wallet-backend/cmd/utils"
Expand All @@ -17,31 +17,9 @@ type ingestCmd struct{}
func (c *ingestCmd) Command() *cobra.Command {
cfg := ingest.Configs{}
cfgOpts := config.ConfigOptions{
{
Name: "database-url",
Usage: "Database connection URL.",
OptType: types.String,
ConfigKey: &cfg.DatabaseURL,
FlagDefault: "postgres://postgres@localhost:5432/wallet-backend?sslmode=disable",
Required: true,
},
{
Name: "network-passphrase",
Usage: "Stellar Network Passphrase to connect.",
OptType: types.String,
ConfigKey: &cfg.NetworkPassphrase,
FlagDefault: network.TestNetworkPassphrase,
Required: true,
},
{
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: &cfg.LogLevel,
CustomSetValue: utils.SetConfigOptionLogLevel,
Required: false,
},
utils.DatabaseURLOption(&cfg.DatabaseURL),
utils.LogLevelOption(&cfg.LogLevel),
utils.NetworkPassphraseOption(&cfg.NetworkPassphrase),
{
Name: "captive-core-bin-path",
Usage: "Path to Captive Core's binary file.",
Expand Down Expand Up @@ -86,27 +64,25 @@ func (c *ingestCmd) Command() *cobra.Command {
}

cmd := &cobra.Command{
Use: "ingest",
Short: "Run Ingestion service",
PersistentPreRun: func(_ *cobra.Command, _ []string) {
cfgOpts.Require()
if err := cfgOpts.SetValues(); err != nil {
log.Fatalf("Error setting values of config options: %s", err.Error())
}
},
Run: func(_ *cobra.Command, _ []string) {
c.Run(cfg)
Use: "ingest",
Short: "Run Ingestion service",
PersistentPreRunE: utils.DefaultPersistentPreRunE(cfgOpts),
RunE: func(_ *cobra.Command, _ []string) error {
return c.Run(cfg)
},
}

if err := cfgOpts.Init(cmd); err != nil {
log.Fatalf("Error initializing a config option: %s", err.Error())
}

return cmd
}

func (c *ingestCmd) Run(cfg ingest.Configs) {
func (c *ingestCmd) Run(cfg ingest.Configs) error {
err := ingest.Ingest(cfg)
if err != nil {
log.Fatalf("Error running Ingest: %s", err.Error())
return fmt.Errorf("running ingest: %w", err)
}
return nil
}
103 changes: 103 additions & 0 deletions cmd/migrate.go
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
daniel-burghardt marked this conversation as resolved.
Show resolved Hide resolved
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"
}
6 changes: 4 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "wallet-backend",
Short: "Wallet Backend Server",
Use: "wallet-backend",
Short: "Wallet Backend Server",
SilenceErrors: true,
Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help()
if err != nil {
Expand All @@ -31,4 +32,5 @@ func init() {

rootCmd.AddCommand((&serveCmd{}).Command())
rootCmd.AddCommand((&ingestCmd{}).Command())
rootCmd.AddCommand((&migrateCmd{}).Command())
}
42 changes: 13 additions & 29 deletions cmd/serve.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cmd

import (
"fmt"
"go/types"

_ "github.com/lib/pq"
Expand All @@ -16,6 +17,8 @@ type serveCmd struct{}
func (c *serveCmd) Command() *cobra.Command {
cfg := serve.Configs{}
cfgOpts := config.ConfigOptions{
utils.DatabaseURLOption(&cfg.DatabaseURL),
utils.LogLevelOption(&cfg.LogLevel),
{
Name: "port",
Usage: "Port to listen and serve on",
Expand All @@ -24,14 +27,6 @@ func (c *serveCmd) Command() *cobra.Command {
FlagDefault: 8000,
Required: false,
},
{
Name: "database-url",
Usage: "Database connection URL",
OptType: types.String,
ConfigKey: &cfg.DatabaseURL,
FlagDefault: "postgres://postgres@localhost:5432/wallet-backend?sslmode=disable",
Required: true,
},
{
Name: "server-base-url",
Usage: "The server base URL",
Expand All @@ -40,15 +35,6 @@ func (c *serveCmd) Command() *cobra.Command {
FlagDefault: "http://localhost:8000",
Required: true,
},
{
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: &cfg.LogLevel,
CustomSetValue: utils.SetConfigOptionLogLevel,
Required: false,
},
{
Name: "wallet-signing-key",
Usage: "The public key of the Stellar account that signs the payloads when making HTTP Request to this server.",
Expand All @@ -59,27 +45,25 @@ func (c *serveCmd) Command() *cobra.Command {
},
}
cmd := &cobra.Command{
Use: "serve",
Short: "Run Wallet Backend server",
PersistentPreRun: func(_ *cobra.Command, _ []string) {
cfgOpts.Require()
if err := cfgOpts.SetValues(); err != nil {
log.Fatalf("Error setting values of config options: %s", err.Error())
}
},
Run: func(_ *cobra.Command, _ []string) {
c.Run(cfg)
Use: "serve",
Short: "Run Wallet Backend server",
PersistentPreRunE: utils.DefaultPersistentPreRunE(cfgOpts),
RunE: func(_ *cobra.Command, _ []string) error {
return c.Run(cfg)
},
}

if err := cfgOpts.Init(cmd); err != nil {
log.Fatalf("Error initializing a config option: %s", err.Error())
}

return cmd
}

func (c *serveCmd) Run(cfg serve.Configs) {
func (c *serveCmd) Run(cfg serve.Configs) error {
err := serve.Serve(cfg)
if err != nil {
log.Fatalf("Error running Serve: %s", err.Error())
return fmt.Errorf("running serve: %w", err)
}
return nil
}
43 changes: 43 additions & 0 deletions cmd/utils/global_options.go
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,
}
}
20 changes: 20 additions & 0 deletions cmd/utils/utils.go
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
}
}
5 changes: 5 additions & 0 deletions internal/db/dbtest/dbtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ func Open(t *testing.T) *dbtest.DB {

return db
}

func OpenWithoutMigrations(t *testing.T) *dbtest.DB {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

db := dbtest.Postgres(t)
return db
}
25 changes: 25 additions & 0 deletions internal/db/migrate.go
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)
}
Loading
Loading