Commit 4eece972 authored by OptimismBot's avatar OptimismBot Committed by GitHub

Merge pull request #7323 from ethereum-optimism/willc/migrate

 feat(indexer): Add migrate command to cli
parents 3c145f46 63ef7041
...@@ -22,5 +22,11 @@ RUN make indexer ...@@ -22,5 +22,11 @@ RUN make indexer
FROM alpine:3.18 FROM alpine:3.18
COPY --from=builder /app/indexer/indexer /usr/local/bin COPY --from=builder /app/indexer/indexer /usr/local/bin
COPY --from=builder /app/indexer/indexer.toml /app/indexer/indexer.toml
COPY --from=builder /app/indexer/migrations /app/indexer/migrations
WORKDIR /app
ENV INDEXER_MIGRATIONS_DIR="/app/indexer/migrations"
CMD ["indexer", "index", "--config", "/app/indexer/indexer.toml"] CMD ["indexer", "index", "--config", "/app/indexer/indexer.toml"]
...@@ -19,6 +19,12 @@ var ( ...@@ -19,6 +19,12 @@ var (
Usage: "path to config file", Usage: "path to config file",
EnvVars: []string{"INDEXER_CONFIG"}, EnvVars: []string{"INDEXER_CONFIG"},
} }
MigrationsFlag = &cli.StringFlag{
Name: "migrations-dir",
Value: "./migrations",
Usage: "path to migrations folder",
EnvVars: []string{"INDEXER_MIGRATIONS_DIR"},
}
) )
func runIndexer(ctx *cli.Context) error { func runIndexer(ctx *cli.Context) error {
...@@ -64,9 +70,30 @@ func runApi(ctx *cli.Context) error { ...@@ -64,9 +70,30 @@ func runApi(ctx *cli.Context) error {
return api.Start(ctx.Context) return api.Start(ctx.Context)
} }
func runMigrations(ctx *cli.Context) error {
log := log.NewLogger(log.ReadCLIConfig(ctx)).New("role", "api")
cfg, err := config.LoadConfig(log, ctx.String(ConfigFlag.Name))
migrationsDir := ctx.String(MigrationsFlag.Name)
if err != nil {
log.Error("failed to load config", "err", err)
return err
}
db, err := database.NewDB(cfg.DB)
if err != nil {
log.Error("failed to connect to database", "err", err)
return err
}
defer db.Close()
return db.ExecuteSQLMigration(migrationsDir)
}
func newCli(GitCommit string, GitDate string) *cli.App { func newCli(GitCommit string, GitDate string) *cli.App {
flags := []cli.Flag{ConfigFlag} flags := []cli.Flag{ConfigFlag}
flags = append(flags, log.CLIFlags("INDEXER")...) flags = append(flags, log.CLIFlags("INDEXER")...)
migrationFlags := []cli.Flag{MigrationsFlag, ConfigFlag}
migrationFlags = append(migrationFlags, log.CLIFlags("INDEXER")...)
return &cli.App{ return &cli.App{
Version: params.VersionWithCommit(GitCommit, GitDate), Version: params.VersionWithCommit(GitCommit, GitDate),
Description: "An indexer of all optimism events with a serving api layer", Description: "An indexer of all optimism events with a serving api layer",
...@@ -84,6 +111,12 @@ func newCli(GitCommit string, GitDate string) *cli.App { ...@@ -84,6 +111,12 @@ func newCli(GitCommit string, GitDate string) *cli.App {
Description: "Runs the indexing service", Description: "Runs the indexing service",
Action: runIndexer, Action: runIndexer,
}, },
{
Name: "migrate",
Flags: migrationFlags,
Description: "Runs the database migrations",
Action: runMigrations,
},
{ {
Name: "version", Name: "version",
Description: "print version", Description: "print version",
......
...@@ -4,6 +4,8 @@ package database ...@@ -4,6 +4,8 @@ package database
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"path/filepath"
"github.com/ethereum-optimism/optimism/indexer/config" "github.com/ethereum-optimism/optimism/indexer/config"
_ "github.com/ethereum-optimism/optimism/indexer/database/serializers" _ "github.com/ethereum-optimism/optimism/indexer/database/serializers"
...@@ -62,7 +64,6 @@ func NewDB(dbConfig config.DBConfig) (*DB, error) { ...@@ -62,7 +64,6 @@ func NewDB(dbConfig config.DBConfig) (*DB, error) {
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to connect to database after multiple retries") return nil, errors.Wrap(err, "failed to connect to database after multiple retries")
} }
db := &DB{ db := &DB{
gorm: gorm, gorm: gorm,
Blocks: newBlocksDB(gorm), Blocks: newBlocksDB(gorm),
...@@ -71,7 +72,6 @@ func NewDB(dbConfig config.DBConfig) (*DB, error) { ...@@ -71,7 +72,6 @@ func NewDB(dbConfig config.DBConfig) (*DB, error) {
BridgeMessages: newBridgeMessagesDB(gorm), BridgeMessages: newBridgeMessagesDB(gorm),
BridgeTransactions: newBridgeTransactionsDB(gorm), BridgeTransactions: newBridgeTransactionsDB(gorm),
} }
return db, nil return db, nil
} }
...@@ -102,3 +102,33 @@ func dbFromGormTx(tx *gorm.DB) *DB { ...@@ -102,3 +102,33 @@ func dbFromGormTx(tx *gorm.DB) *DB {
BridgeTransactions: newBridgeTransactionsDB(tx), BridgeTransactions: newBridgeTransactionsDB(tx),
} }
} }
func (db *DB) ExecuteSQLMigration(migrationsFolder string) error {
err := filepath.Walk(migrationsFolder, func(path string, info os.FileInfo, err error) error {
// Check for any walking error
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Failed to process migration file: %s", path))
}
// Skip directories
if info.IsDir() {
return nil
}
// Read the migration file content
fileContent, readErr := os.ReadFile(path)
if readErr != nil {
return errors.Wrap(readErr, fmt.Sprintf("Error reading SQL file: %s", path))
}
// Execute the migration
execErr := db.gorm.Exec(string(fileContent)).Error
if execErr != nil {
return errors.Wrap(execErr, fmt.Sprintf("Error executing SQL script: %s", path))
}
return nil
})
return err
}
...@@ -17,6 +17,27 @@ services: ...@@ -17,6 +17,27 @@ services:
- postgres_data:/data/postgres - postgres_data:/data/postgres
- ./migrations:/docker-entrypoint-initdb.d/ - ./migrations:/docker-entrypoint-initdb.d/
migrations:
build:
context: ..
dockerfile: indexer/Dockerfile
command: ["indexer", "migrate"]
environment:
- INDEXER_RPC_URL_L1=$INDEXER_RPC_URL_L1
- INDEXER_RPC_URL_L2=$INDEXER_RPC_URL_L2
- INDEXER_CONFIG=/indexer/indexer.toml
- INDEXER_CHAIN_PRESET=$INDEXER_CHAIN_PRESET
- INDEXER_DB_PORT=5432
- INDEXER_DB_HOST=postgres
- INDEXER_DB_USER=db_username
- INDEXER_DB_PASS=db_password
- INDEXER_DB_NAME=db_name
volumes:
- ./indexer.toml:/indexer/indexer.toml
depends_on:
postgres:
condition: service_healthy
indexer: indexer:
build: build:
context: .. context: ..
...@@ -26,11 +47,21 @@ services: ...@@ -26,11 +47,21 @@ services:
- INDEXER_RPC_URL_L1=$INDEXER_RPC_URL_L1 - INDEXER_RPC_URL_L1=$INDEXER_RPC_URL_L1
- INDEXER_RPC_URL_L2=$INDEXER_RPC_URL_L2 - INDEXER_RPC_URL_L2=$INDEXER_RPC_URL_L2
- INDEXER_CONFIG=/indexer/indexer.toml - INDEXER_CONFIG=/indexer/indexer.toml
- INDEXER_CHAIN_PRESET=$INDEXER_CHAIN_PRESET
- INDEXER_DB_PORT=5432
- INDEXER_DB_HOST=postgres
- INDEXER_DB_USER=db_username
- INDEXER_DB_PASS=db_password
- INDEXER_DB_NAME=db_name
volumes: volumes:
- ./indexer.toml:/indexer/indexer.toml - ./indexer.toml:/indexer/indexer.toml
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
depends_on:
migrations:
condition: service_started
api: api:
build: build:
...@@ -45,6 +76,12 @@ services: ...@@ -45,6 +76,12 @@ services:
- INDEXER_RPC_URL_L1=$INDEXER_RPC_URL_L1 - INDEXER_RPC_URL_L1=$INDEXER_RPC_URL_L1
- INDEXER_RPC_URL_L2=$INDEXER_RPC_URL_L2 - INDEXER_RPC_URL_L2=$INDEXER_RPC_URL_L2
- INDEXER_CONFIG=/indexer/indexer.toml - INDEXER_CONFIG=/indexer/indexer.toml
- INDEXER_CHAIN_PRESET=$INDEXER_CHAIN_PRESET
- INDEXER_DB_HOST=postgres
- INDEXER_DB_PORT=5432
- INDEXER_DB_USER=db_username
- INDEXER_DB_PASS=db_password
- INDEXER_DB_NAME=db_name
volumes: volumes:
- ./indexer.toml:/indexer/indexer.toml - ./indexer.toml:/indexer/indexer.toml
ports: ports:
...@@ -83,6 +120,7 @@ services: ...@@ -83,6 +120,7 @@ services:
backend-goerli: backend-goerli:
image: ethereumoptimism/gateway-backend:latest image: ethereumoptimism/gateway-backend:latest
platform: linux/amd64
environment: environment:
# this enables the backend to proxy history requests to the indexer # this enables the backend to proxy history requests to the indexer
- BRIDGE_INDEXER_URI=http://api - BRIDGE_INDEXER_URI=http://api
......
...@@ -4,9 +4,7 @@ import ( ...@@ -4,9 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"io/fs"
"os" "os"
"path/filepath"
"testing" "testing"
"time" "time"
...@@ -132,31 +130,20 @@ func setupTestDatabase(t *testing.T) string { ...@@ -132,31 +130,20 @@ func setupTestDatabase(t *testing.T) string {
pg.Close() pg.Close()
}) })
// setup schema, migration files ware walked in lexical order dbConfig := config.DBConfig{
t.Logf("created database %s", dbName) Host: "127.0.0.1",
db, err := sql.Open("pgx", fmt.Sprintf("postgres://%s@localhost:5432/%s?sslmode=disable", user, dbName)) Port: 5432,
Name: dbName,
User: user,
Password: "",
}
// NewDB will create the database schema
db, err := database.NewDB(dbConfig)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, db.Ping())
defer db.Close() defer db.Close()
err = db.ExecuteSQLMigration("../migrations")
require.NoError(t, err)
t.Logf("running schema migrations...") t.Logf("database %s setup and migrations executed", dbName)
require.NoError(t, filepath.Walk("../migrations", func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
} else if info.IsDir() {
return nil
}
t.Logf("running schema migration: %s", path)
data, err := os.ReadFile(path)
if err != nil {
return err
}
_, err = db.Exec(string(data))
return err
}))
t.Logf("schema loaded")
return dbName return dbName
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment