2018-06-24 16:40:48 +03:00
package commands
import (
2018-07-09 16:47:38 +03:00
"fmt"
2021-10-08 16:29:10 +03:00
2021-06-21 16:54:08 +03:00
"github.com/pkg/errors"
2018-07-09 16:47:38 +03:00
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/metadatautil"
2021-03-08 14:59:35 +03:00
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
"github.com/hasura/graphql-engine/cli/v2/internal/scripts"
2021-02-17 07:20:19 +03:00
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2"
"github.com/hasura/graphql-engine/cli/v2/migrate"
mig "github.com/hasura/graphql-engine/cli/v2/migrate/cmd"
"github.com/hasura/graphql-engine/cli/v2/util"
2018-06-24 16:40:48 +03:00
"github.com/spf13/cobra"
2020-02-24 19:14:46 +03:00
"github.com/spf13/viper"
2019-02-14 12:37:47 +03:00
// Initialize migration drivers
2021-06-16 14:44:15 +03:00
_ "github.com/hasura/graphql-engine/cli/v2/migrate/database/hasuradb"
_ "github.com/hasura/graphql-engine/cli/v2/migrate/source/file"
2018-06-24 16:40:48 +03:00
)
2019-02-14 12:37:47 +03:00
// NewMigrateCmd returns the migrate command
2018-06-24 16:40:48 +03:00
func NewMigrateCmd ( ec * cli . ExecutionContext ) * cobra . Command {
2020-02-24 19:14:46 +03:00
v := viper . New ( )
2018-06-24 16:40:48 +03:00
migrateCmd := & cobra . Command {
Use : "migrate" ,
Short : "Manage migrations on the database" ,
SilenceUsage : true ,
2020-02-24 19:14:46 +03:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2020-03-26 06:08:54 +03:00
cmd . Root ( ) . PersistentPreRun ( cmd , args )
2020-03-03 10:06:59 +03:00
ec . Viper = v
2020-02-24 19:14:46 +03:00
err := ec . Prepare ( )
if err != nil {
return err
}
2021-01-18 20:11:05 +03:00
if err := ec . Validate ( ) ; err != nil {
return err
}
return nil
2020-02-24 19:14:46 +03:00
} ,
2018-06-24 16:40:48 +03:00
}
2020-02-24 19:14:46 +03:00
2020-04-08 14:55:42 +03:00
f := migrateCmd . PersistentFlags ( )
2021-03-09 09:43:41 +03:00
f . StringVar ( & ec . Source . Name , "database-name" , "" , "database on which operation should be applied" )
2020-04-08 14:55:42 +03:00
2021-03-15 18:40:52 +03:00
f . String ( "endpoint" , "" , "http(s) endpoint for Hasura GraphQL engine" )
f . String ( "admin-secret" , "" , "admin secret for Hasura GraphQL engine" )
f . String ( "access-key" , "" , "access key for Hasura GraphQL engine" )
2021-10-13 17:38:07 +03:00
if err := f . MarkDeprecated ( "access-key" , "use --admin-secret instead" ) ; err != nil {
ec . Logger . WithError ( err ) . Errorf ( "error while using a dependency library" )
}
2020-04-28 14:59:57 +03:00
f . Bool ( "insecure-skip-tls-verify" , false , "skip TLS verification and disable cert checking (default: false)" )
f . String ( "certificate-authority" , "" , "path to a cert file for the certificate authority" )
2021-06-09 10:12:56 +03:00
f . Bool ( "disable-interactive" , false , "disables interactive prompts (default: false)" )
2020-04-08 14:55:42 +03:00
util . BindPFlag ( v , "endpoint" , f . Lookup ( "endpoint" ) )
util . BindPFlag ( v , "admin_secret" , f . Lookup ( "admin-secret" ) )
util . BindPFlag ( v , "access_key" , f . Lookup ( "access-key" ) )
2020-04-28 14:59:57 +03:00
util . BindPFlag ( v , "insecure_skip_tls_verify" , f . Lookup ( "insecure-skip-tls-verify" ) )
util . BindPFlag ( v , "certificate_authority" , f . Lookup ( "certificate-authority" ) )
2021-06-09 10:12:56 +03:00
util . BindPFlag ( v , "disable_interactive" , f . Lookup ( "disable-interactive" ) )
2020-04-08 14:55:42 +03:00
2021-05-17 18:19:15 +03:00
f . BoolVar ( & ec . DisableAutoStateMigration , "disable-auto-state-migration" , false , "after a config v3 update, disable automatically moving state from hdb_catalog.schema_migrations to catalog state" )
2021-10-13 17:38:07 +03:00
if err := f . MarkHidden ( "disable-auto-state-migration" ) ; err != nil {
ec . Logger . WithError ( err ) . Errorf ( "error while using a dependency library" )
}
2021-05-17 18:19:15 +03:00
2021-01-18 20:11:05 +03:00
migrateCmd . AddCommand (
newMigrateApplyCmd ( ec ) ,
newMigrateStatusCmd ( ec ) ,
newMigrateCreateCmd ( ec ) ,
newMigrateSquashCmd ( ec ) ,
2021-05-24 05:33:45 +03:00
newMigrateDeleteCmd ( ec ) ,
2021-01-18 20:11:05 +03:00
)
2018-06-24 16:40:48 +03:00
return migrateCmd
}
2018-07-09 16:47:38 +03:00
2021-06-21 16:54:08 +03:00
var errDatabaseNotFound = errors . New ( "database not found" )
var errDatabaseNameNotSet = errors . New ( "--database-name flag is required" )
2019-02-14 12:37:47 +03:00
// ExecuteMigration runs the actual migration
2018-08-21 21:37:47 +03:00
func ExecuteMigration ( cmd string , t * migrate . Migrate , stepOrVersion int64 ) error {
2018-07-09 16:47:38 +03:00
var err error
switch cmd {
case "up" :
err = mig . UpCmd ( t , stepOrVersion )
case "down" :
err = mig . DownCmd ( t , stepOrVersion )
2020-02-03 10:03:32 +03:00
case "gotoVersion" :
err = mig . GotoVersionCmd ( t , stepOrVersion )
2018-07-09 16:47:38 +03:00
case "version" :
var direction string
if stepOrVersion >= 0 {
direction = "up"
} else {
direction = "down"
stepOrVersion = - ( stepOrVersion )
}
err = mig . GotoCmd ( t , uint64 ( stepOrVersion ) , direction )
default :
2021-01-18 20:11:05 +03:00
err = fmt . Errorf ( "invalid command" )
2018-07-09 16:47:38 +03:00
}
return err
}
func executeStatus ( t * migrate . Migrate ) ( * migrate . Status , error ) {
status , err := t . GetStatus ( )
if err != nil {
return nil , err
}
return status , nil
}
2021-04-01 15:58:24 +03:00
func validateConfigV3Flags ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
2021-12-23 18:58:53 +03:00
if err := validateConfigV3Prechecks ( cmd , ec ) ; err != nil {
return err
}
2021-06-09 10:12:56 +03:00
if ec . Config . Version < cli . V3 {
return nil
}
2021-12-23 18:58:53 +03:00
if err := databaseChooser ( ec ) ; err != nil {
return err
}
if err := validateSourceInfo ( ec ) ; err != nil {
return err
}
// check if migration ops are supported for the database
if ! migrate . IsMigrationsSupported ( ec . Source . Kind ) {
return fmt . Errorf ( "migrations on source %s of kind %s is not supported" , ec . Source . Name , ec . Source . Kind )
}
return nil
}
func validateConfigV3FlagsWithAll ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
if err := validateConfigV3Prechecks ( cmd , ec ) ; err != nil {
return err
}
if ec . Config . Version < cli . V3 {
return nil
}
2021-06-09 10:12:56 +03:00
// if --all-databases flag is present, ignore --database-name and showing UI prompt for choosing a single database
if cmd . Flags ( ) . Changed ( "all-databases" ) {
return nil
}
2021-12-23 18:58:53 +03:00
if err := databaseChooserWithAllOption ( ec ) ; err != nil {
return err
2021-10-08 16:29:10 +03:00
}
2021-12-23 18:58:53 +03:00
if ec . AllDatabases {
return nil
2021-10-08 16:29:10 +03:00
}
2021-12-23 18:58:53 +03:00
if err := validateSourceInfo ( ec ) ; err != nil {
return err
2021-06-09 10:12:56 +03:00
}
2021-12-23 18:58:53 +03:00
// check if migration ops are supported for the database
if ! migrate . IsMigrationsSupported ( ec . Source . Kind ) {
return fmt . Errorf ( "migrations on source %s of kind %s is not supported" , ec . Source . Name , ec . Source . Kind )
}
return nil
}
func validateConfigV3Prechecks ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
// for project using config older than v3, use PG source kind
if ec . Config . Version < cli . V3 {
ec . Source . Kind = hasura . SourceKindPG
if err := scripts . CheckIfUpdateToConfigV3IsRequired ( ec ) ; err != nil {
2021-06-09 10:12:56 +03:00
return err
}
2021-12-23 18:58:53 +03:00
return nil
}
// for project using config equal to or greater than v3
// database-name flag is required when running in non-terminal mode
if ( ! ec . IsTerminal || ec . Config . DisableInteractive ) && ! cmd . Flags ( ) . Changed ( "all-databases" ) && ! cmd . Flags ( ) . Changed ( "database-name" ) {
return errDatabaseNameNotSet
2021-06-09 10:12:56 +03:00
}
2021-12-23 18:58:53 +03:00
return nil
}
func validateSourceInfo ( ec * cli . ExecutionContext ) error {
2021-06-09 10:12:56 +03:00
// find out the database kind by making a API call to server
// and update ec to include the database name and kind
sourceKind , err := metadatautil . GetSourceKind ( ec . APIClient . V1Metadata . ExportMetadata , ec . Source . Name )
if err != nil {
return fmt . Errorf ( "determining database kind of %s: %w" , ec . Source . Name , err )
}
if sourceKind == nil {
2021-06-21 16:54:08 +03:00
return fmt . Errorf ( "%w: error determining database kind for %s, check if database exists on hasura" , errDatabaseNotFound , ec . Source . Name )
2021-06-09 10:12:56 +03:00
}
ec . Source . Kind = * sourceKind
2021-12-23 18:58:53 +03:00
return nil
}
2021-06-09 10:12:56 +03:00
2021-12-23 18:58:53 +03:00
func databaseChooser ( ec * cli . ExecutionContext ) error {
// prompt UI for choosing database if source name is not set
if ec . Source . Name == "" {
databaseName , err := metadatautil . DatabaseChooserUI ( ec . APIClient . V1Metadata . ExportMetadata )
if err != nil {
return err
}
ec . Source . Name = databaseName
}
return nil
}
func databaseChooserWithAllOption ( ec * cli . ExecutionContext ) error {
// prompt UI for choosing database if source name is not set
if ec . Source . Name == "" {
databaseName , err := metadatautil . DatabaseChooserUIWithAll ( ec . APIClient . V1Metadata . ExportMetadata )
if err != nil {
return err
}
if databaseName == metadatautil . ChooseAllDatabases {
ec . AllDatabases = true
return nil
}
ec . Source . Name = databaseName
2021-04-01 15:58:24 +03:00
}
return nil
}