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
2022-11-03 08:54:29 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
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 {
2023-01-09 07:25:53 +03:00
Use : "migrate" ,
Short : "Manage migrations on the database" ,
Long : ` This command , when used with a collection of subcommands , allows you to manage migrations on the database .
Further reading :
- https : //hasura.io/docs/latest/migrations-metadata-seeds/manage-migrations/
` ,
2018-06-24 16:40:48 +03:00
SilenceUsage : true ,
2020-02-24 19:14:46 +03:00
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-03 08:54:29 +03:00
op := genOpName ( cmd , "PersistentPreRunE" )
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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2020-02-24 19:14:46 +03:00
}
2021-01-18 20:11:05 +03:00
if err := ec . Validate ( ) ; err != nil {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-01-18 20:11:05 +03:00
}
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
2023-01-11 11:37:47 +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
2022-11-03 08:54:29 +03:00
var errDatabaseNotFound = fmt . Errorf ( "database not found" )
var errDatabaseNameNotSet = fmt . Errorf ( "--database-name flag is required" )
2021-06-21 16:54:08 +03:00
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 {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.ExecuteMigration"
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
}
2022-11-03 08:54:29 +03:00
if err != nil {
return errors . E ( op , err )
}
return nil
2018-07-09 16:47:38 +03:00
}
func executeStatus ( t * migrate . Migrate ) ( * migrate . Status , error ) {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.executeStatus"
2018-07-09 16:47:38 +03:00
status , err := t . GetStatus ( )
if err != nil {
2022-11-03 08:54:29 +03:00
return nil , errors . E ( op , err )
2018-07-09 16:47:38 +03:00
}
return status , nil
}
2021-04-01 15:58:24 +03:00
func validateConfigV3Flags ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.validateConfigV3Flags"
2021-12-23 18:58:53 +03:00
if err := validateConfigV3Prechecks ( cmd , ec ) ; err != nil {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
if err := validateSourceInfo ( ec ) ; err != nil {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
// check if migration ops are supported for the database
if ! migrate . IsMigrationsSupported ( ec . Source . Kind ) {
2023-01-09 11:01:49 +03:00
return errors . E ( op , fmt . Errorf ( "migrations on database '%s' of kind '%s' is not supported" , ec . Source . Name , ec . Source . Kind ) )
2021-12-23 18:58:53 +03:00
}
return nil
}
func validateConfigV3FlagsWithAll ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.validateConfigV3FlagsWithAll"
2021-12-23 18:58:53 +03:00
if err := validateConfigV3Prechecks ( cmd , ec ) ; err != nil {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , 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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , 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 ) {
2023-01-09 11:01:49 +03:00
return errors . E ( op , fmt . Errorf ( "migrations on database '%s' of kind '%s' is not supported" , ec . Source . Name , ec . Source . Kind ) )
2021-12-23 18:58:53 +03:00
}
return nil
}
func validateConfigV3Prechecks ( cmd * cobra . Command , ec * cli . ExecutionContext ) error {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.validateConfigV3Prechecks"
2021-12-23 18:58:53 +03:00
// 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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-06-09 10:12:56 +03:00
}
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" ) {
2022-11-03 08:54:29 +03:00
return errors . E ( op , 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 {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.validateSourceInfo"
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 {
2023-01-09 11:01:49 +03:00
return errors . E ( op , fmt . Errorf ( "determining database kind of '%s': %w" , ec . Source . Name , err ) )
2021-06-09 10:12:56 +03:00
}
if sourceKind == nil {
2023-01-09 11:01:49 +03:00
return errors . E ( op , 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 {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.databaseChooser"
2021-12-23 18:58:53 +03:00
// 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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
ec . Source . Name = databaseName
}
return nil
}
func databaseChooserWithAllOption ( ec * cli . ExecutionContext ) error {
2022-11-03 08:54:29 +03:00
var op errors . Op = "commands.databaseChooserWithAllOption"
2021-12-23 18:58:53 +03:00
// 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 {
2022-11-03 08:54:29 +03:00
return errors . E ( op , err )
2021-12-23 18:58:53 +03:00
}
if databaseName == metadatautil . ChooseAllDatabases {
ec . AllDatabases = true
return nil
}
ec . Source . Name = databaseName
2021-04-01 15:58:24 +03:00
}
return nil
}