mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[NOD-327] Add --migrate cli flag to API server (#407)
* [NOD-327] Add --migrate cli flag to API server * [NOD-327] Change log messages * [NOD-327] Remove `required` flag from API server RPC CLI arguments * [NOD-327] Add database version in migrations logs
This commit is contained in:
parent
f4850b9e7a
commit
2429b623fc
@ -23,9 +23,9 @@ var (
|
|||||||
// Config defines the configuration options for the API server.
|
// Config defines the configuration options for the API server.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
LogDir string `long:"logdir" description:"Directory to log output."`
|
LogDir string `long:"logdir" description:"Directory to log output."`
|
||||||
RPCUser string `short:"u" long:"rpcuser" description:"RPC username" required:"true"`
|
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
|
||||||
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password" required:"true"`
|
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
|
||||||
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to" required:"true"`
|
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
|
||||||
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
|
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
|
||||||
DisableTLS bool `long:"notls" description:"Disable TLS"`
|
DisableTLS bool `long:"notls" description:"Disable TLS"`
|
||||||
DBAddress string `long:"dbaddress" description:"Database address"`
|
DBAddress string `long:"dbaddress" description:"Database address"`
|
||||||
@ -33,6 +33,7 @@ type Config struct {
|
|||||||
DBPassword string `long:"dbpass" description:"Database password" required:"true"`
|
DBPassword string `long:"dbpass" description:"Database password" required:"true"`
|
||||||
DBName string `long:"dbname" description:"Database name" required:"true"`
|
DBName string `long:"dbname" description:"Database name" required:"true"`
|
||||||
HTTPListen string `long:"listen" description:"HTTP address to listen on (default: 0.0.0.0:8080)"`
|
HTTPListen string `long:"listen" description:"HTTP address to listen on (default: 0.0.0.0:8080)"`
|
||||||
|
Migrate bool `long:"migrate" description:"Migrate the database to the latest version. The server will not start when using this flag."`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses the CLI arguments and returns a config struct.
|
// Parse parses the CLI arguments and returns a config struct.
|
||||||
@ -49,6 +50,18 @@ func Parse() (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !cfg.Migrate {
|
||||||
|
if cfg.RPCUser == "" {
|
||||||
|
return nil, errors.New("--rpcuser is required if --migrate flag is not used")
|
||||||
|
}
|
||||||
|
if cfg.RPCPassword == "" {
|
||||||
|
return nil, errors.New("--rpcpass is required if --migrate flag is not used")
|
||||||
|
}
|
||||||
|
if cfg.RPCServer == "" {
|
||||||
|
return nil, errors.New("--rpcserver is required if --migrate flag is not used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.RPCCert == "" && !cfg.DisableTLS {
|
if cfg.RPCCert == "" && !cfg.DisableTLS {
|
||||||
return nil, errors.New("--notls has to be disabled if --cert is used")
|
return nil, errors.New("--notls has to be disabled if --cert is used")
|
||||||
}
|
}
|
||||||
|
@ -34,13 +34,17 @@ func (l gormLogger) Print(v ...interface{}) {
|
|||||||
// config variable.
|
// config variable.
|
||||||
func Connect(cfg *config.Config) error {
|
func Connect(cfg *config.Config) error {
|
||||||
connectionString := buildConnectionString(cfg)
|
connectionString := buildConnectionString(cfg)
|
||||||
isCurrent, err := isCurrent(connectionString)
|
migrator, driver, err := openMigrator(connectionString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isCurrent, version, err := isCurrent(migrator, driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error checking whether the database is current: %s", err)
|
return fmt.Errorf("Error checking whether the database is current: %s", err)
|
||||||
}
|
}
|
||||||
if !isCurrent {
|
if !isCurrent {
|
||||||
return fmt.Errorf("Database is not current. Please migrate" +
|
return fmt.Errorf("Database is not current (version %d). Please migrate"+
|
||||||
" the database and start again.")
|
" the database by running the server with --migrate flag and then run it again.", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err = gorm.Open("mysql", connectionString)
|
db, err = gorm.Open("mysql", connectionString)
|
||||||
@ -68,35 +72,68 @@ func buildConnectionString(cfg *config.Config) string {
|
|||||||
|
|
||||||
// isCurrent resolves whether the database is on the latest
|
// isCurrent resolves whether the database is on the latest
|
||||||
// version of the schema.
|
// version of the schema.
|
||||||
func isCurrent(connectionString string) (bool, error) {
|
func isCurrent(migrator *migrate.Migrate, driver source.Driver) (bool, uint, error) {
|
||||||
driver, err := source.Open("file://migrations")
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
migrator, err := migrate.NewWithSourceInstance(
|
|
||||||
"migrations", driver, "mysql://"+connectionString)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current version
|
// Get the current version
|
||||||
version, isDirty, err := migrator.Version()
|
version, isDirty, err := migrator.Version()
|
||||||
if err == migrate.ErrNilVersion {
|
if err == migrate.ErrNilVersion {
|
||||||
return false, nil
|
return false, 0, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, 0, err
|
||||||
}
|
}
|
||||||
if isDirty {
|
if isDirty {
|
||||||
return false, fmt.Errorf("Database is dirty")
|
return false, 0, fmt.Errorf("Database is dirty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The database is current if Next returns ErrNotExist
|
// The database is current if Next returns ErrNotExist
|
||||||
_, err = driver.Next(version)
|
_, err = driver.Next(version)
|
||||||
if pathErr, ok := err.(*os.PathError); ok {
|
if pathErr, ok := err.(*os.PathError); ok {
|
||||||
if pathErr.Err == os.ErrNotExist {
|
if pathErr.Err == os.ErrNotExist {
|
||||||
return true, nil
|
return true, version, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false, err
|
return false, version, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func openMigrator(connectionString string) (*migrate.Migrate, source.Driver, error) {
|
||||||
|
driver, err := source.Open("file://migrations")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
migrator, err := migrate.NewWithSourceInstance(
|
||||||
|
"migrations", driver, "mysql://"+connectionString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return migrator, driver, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate database to the latest version.
|
||||||
|
func Migrate(cfg *config.Config) error {
|
||||||
|
connectionString := buildConnectionString(cfg)
|
||||||
|
migrator, driver, err := openMigrator(connectionString)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isCurrent, version, err := isCurrent(migrator, driver)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error checking whether the database is current: %s", err)
|
||||||
|
}
|
||||||
|
if isCurrent {
|
||||||
|
log.Infof("Database is already up-to-date (version %d)", version)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = migrator.Up()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
version, isDirty, err := migrator.Version()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if isDirty {
|
||||||
|
return fmt.Errorf("error migrating database: database is dirty")
|
||||||
|
}
|
||||||
|
log.Infof("Migrated database to the latest version (version %d)", version)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,14 @@ func main() {
|
|||||||
panic(fmt.Errorf("Error parsing command-line arguments: %s", err))
|
panic(fmt.Errorf("Error parsing command-line arguments: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Migrate {
|
||||||
|
err := database.Migrate(cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Error migrating database: %s", err))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = database.Connect(cfg)
|
err = database.Connect(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("Error connecting to database: %s", err))
|
panic(fmt.Errorf("Error connecting to database: %s", err))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user