2018-06-24 16:40:48 +03:00
package commands
import (
2021-04-01 08:13:24 +03:00
"fmt"
2021-01-18 20:11:05 +03:00
"path/filepath"
2018-06-24 16:40:48 +03:00
"time"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2"
2021-07-23 12:49:44 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/hasura"
2021-06-16 14:44:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/migrate"
2018-06-24 16:40:48 +03:00
"github.com/pkg/errors"
"github.com/spf13/cobra"
2019-03-18 19:40:04 +03:00
"github.com/spf13/pflag"
2019-04-30 11:34:08 +03:00
2021-06-16 14:44:15 +03:00
mig "github.com/hasura/graphql-engine/cli/v2/migrate/cmd"
2019-04-30 11:34:08 +03:00
log "github.com/sirupsen/logrus"
2018-06-24 16:40:48 +03:00
)
2019-04-30 11:34:08 +03:00
const migrateCreateCmdExamples = ` # Setup migration files for the first time by introspecting a server :
2019-12-12 08:16:36 +03:00
hasura migrate create "init" -- from - server
# Use with admin secret :
hasura migrate create -- admin - secret "<admin-secret>"
# Setup migration files from an instance mentioned by the flag :
2020-04-09 12:05:05 +03:00
hasura migrate create init -- from - server -- endpoint "<endpoint>"
# Take pg_dump of schema and hasura metadata from server while specifying the schemas to include
hasura migrate create init -- from - server -- schema myschema1 , myschema2
# Take pg_dump from server and save it as a migration and specify the schemas to include
2020-06-16 09:00:20 +03:00
hasura migrate create init -- sql - from - server -- schema myschema1 , myschema2
# Create up and down SQL migrations , providing contents as flags
hasura migrate create migration - name -- up - sql "CREATE TABLE article(id serial NOT NULL, title text NOT NULL, content text NOT NULL);" -- down - sql "DROP TABLE article;"
`
2019-04-30 11:34:08 +03:00
2018-06-28 11:36:25 +03:00
func newMigrateCreateCmd ( ec * cli . ExecutionContext ) * cobra . Command {
2018-06-24 16:40:48 +03:00
opts := & migrateCreateOptions {
EC : ec ,
}
migrateCreateCmd := & cobra . Command {
Use : "create [migration-name]" ,
Short : "Create files required for a migration" ,
2021-03-15 18:40:52 +03:00
Long : "Create ``sql`` and ``yaml`` files required for a migration" ,
2019-04-30 11:34:08 +03:00
Example : migrateCreateCmdExamples ,
2018-06-24 16:40:48 +03:00
SilenceUsage : true ,
Args : cobra . ExactArgs ( 1 ) ,
2021-04-01 08:13:24 +03:00
PreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
if cmd . Flags ( ) . Changed ( "metadata-from-server" ) {
2021-09-10 07:54:48 +03:00
return fmt . Errorf ( "metadata-from-server flag is deprecated" )
2021-04-01 08:13:24 +03:00
}
if cmd . Flags ( ) . Changed ( "metadata-from-file" ) {
2021-09-10 07:54:48 +03:00
return fmt . Errorf ( "metadata-from-file flag is deprecated" )
2021-04-01 08:13:24 +03:00
}
2021-06-21 16:54:08 +03:00
if err := validateConfigV3Flags ( cmd , ec ) ; err != nil {
if errors . Is ( err , errDatabaseNotFound ) {
// this means provided database is not yet connected to hasura
// this can be ignored for `migrate create`
// we can allow users to create migration files for databases
// which are not connected
ec . Logger . Warnf ( "database %s is not connected to hasura" , ec . Source . Name )
ec . Source . Kind = hasura . SourceKindPG // the default kind is postgres
return nil
}
return err
}
return nil
2021-04-01 08:13:24 +03:00
} ,
2018-06-24 16:40:48 +03:00
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
opts . name = args [ 0 ]
2019-04-03 14:29:58 +03:00
opts . EC . Spin ( "Creating migration files..." )
2021-03-08 14:59:35 +03:00
opts . Source = ec . Source
2019-04-03 14:29:58 +03:00
version , err := opts . run ( )
opts . EC . Spinner . Stop ( )
if err != nil {
return err
}
opts . EC . Logger . WithFields ( log . Fields {
"version" : version ,
"name" : opts . name ,
} ) . Info ( "Migrations files created" )
return nil
2018-06-24 16:40:48 +03:00
} ,
}
2019-03-18 19:40:04 +03:00
f := migrateCreateCmd . Flags ( )
opts . flags = f
2020-04-09 12:05:05 +03:00
f . BoolVar ( & opts . fromServer , "from-server" , false , "take pg_dump of schema (default: public) and Hasura metadata from the server" )
2021-03-15 18:40:52 +03:00
f . StringVar ( & opts . sqlFile , "sql-from-file" , "" , "path to an SQL file which contains the SQL statements" )
f . BoolVar ( & opts . sqlServer , "sql-from-server" , false , "take pg_dump from the server (default: public) and save it as a migration" )
2020-04-09 12:05:05 +03:00
f . StringSliceVar ( & opts . schemaNames , "schema" , [ ] string { "public" } , "name of Postgres schema to export as a migration. provide multiple schemas with a comma separated list e.g. --schema public,user" )
2019-03-18 19:40:04 +03:00
f . StringVar ( & opts . metaDataFile , "metadata-from-file" , "" , "path to a hasura metadata file to be used for up actions" )
f . BoolVar ( & opts . metaDataServer , "metadata-from-server" , false , "take metadata from the server and write it as an up migration file" )
2020-06-16 09:00:20 +03:00
f . StringVar ( & opts . upSQL , "up-sql" , "" , "sql string/query that is to be used to create an up migration" )
f . StringVar ( & opts . downSQL , "down-sql" , "" , "sql string/query that is to be used to create a down migration" )
2019-04-30 11:34:08 +03:00
2019-03-18 19:40:04 +03:00
migrateCreateCmd . MarkFlagFilename ( "sql-from-file" )
migrateCreateCmd . MarkFlagFilename ( "metadata-from-file" )
2018-06-24 16:40:48 +03:00
return migrateCreateCmd
}
type migrateCreateOptions struct {
EC * cli . ExecutionContext
2019-03-18 19:40:04 +03:00
name string
flags * pflag . FlagSet
// Flags
2019-04-30 11:34:08 +03:00
fromServer bool
2019-03-18 19:40:04 +03:00
sqlFile string
2019-04-30 11:34:08 +03:00
sqlServer bool
2019-03-18 19:40:04 +03:00
metaDataFile string
metaDataServer bool
2019-04-30 11:34:08 +03:00
schemaNames [ ] string
2020-06-16 09:00:20 +03:00
upSQL string
downSQL string
2021-03-08 14:59:35 +03:00
Source cli . Source
2018-06-24 16:40:48 +03:00
}
2019-04-03 14:29:58 +03:00
func ( o * migrateCreateOptions ) run ( ) ( version int64 , err error ) {
2018-06-24 16:40:48 +03:00
timestamp := getTime ( )
2021-03-08 14:59:35 +03:00
createOptions := mig . New ( timestamp , o . name , filepath . Join ( o . EC . MigrationDir , o . Source . Name ) )
2019-03-18 19:40:04 +03:00
2019-04-30 11:34:08 +03:00
if o . fromServer {
o . sqlServer = true
2020-02-25 09:46:11 +03:00
if o . EC . Config . Version == cli . V1 {
o . metaDataServer = true
}
}
if o . flags . Changed ( "metadata-from-file" ) && o . EC . Config . Version != cli . V1 {
return 0 , errors . New ( "metadata-from-file flag can be set only with config version 1" )
}
if o . flags . Changed ( "metadata-from-server" ) && o . EC . Config . Version != cli . V1 {
return 0 , errors . New ( "metadata-from-server flag can be set only with config version 1" )
2019-04-30 11:34:08 +03:00
}
if o . flags . Changed ( "metadata-from-file" ) && o . sqlServer {
return 0 , errors . New ( "only one sql type can be set" )
}
2020-06-16 09:00:20 +03:00
2019-04-30 11:34:08 +03:00
if o . flags . Changed ( "metadata-from-file" ) && o . metaDataServer {
return 0 , errors . New ( "only one metadata type can be set" )
}
2020-06-16 09:00:20 +03:00
if o . flags . Changed ( "up-sql" ) && ! o . flags . Changed ( "down-sql" ) {
o . EC . Logger . Warn ( "you are creating an up migration without a down migration" )
}
if o . flags . Changed ( "down-sql" ) && ! o . flags . Changed ( "up-sql" ) {
o . EC . Logger . Warn ( "you are creating a down migration without an up migration" )
}
2019-04-30 11:34:08 +03:00
var migrateDrv * migrate . Migrate
2021-06-21 16:54:08 +03:00
// disabling auto state migrations for migrate create command
o . EC . DisableAutoStateMigration = true
2020-06-16 09:00:20 +03:00
if o . sqlServer || o . metaDataServer || o . flags . Changed ( "up-sql" ) || o . flags . Changed ( "down-sql" ) {
2021-03-08 14:59:35 +03:00
migrateDrv , err = migrate . NewMigrate ( o . EC , true , o . Source . Name , o . Source . Kind )
2019-04-30 11:34:08 +03:00
if err != nil {
return 0 , errors . Wrap ( err , "cannot create migrate instance" )
}
}
2019-03-18 19:40:04 +03:00
if o . flags . Changed ( "sql-from-file" ) {
// sql-file flag is set
err := createOptions . SetSQLUpFromFile ( o . sqlFile )
if err != nil {
2019-04-03 14:29:58 +03:00
return 0 , errors . Wrap ( err , "cannot set sql file" )
2019-03-18 19:40:04 +03:00
}
}
2019-04-30 11:34:08 +03:00
if o . sqlServer {
2021-06-14 15:27:45 +03:00
data , err := migrateDrv . ExportSchemaDump ( o . schemaNames , o . Source . Name , o . Source . Kind )
2019-04-30 11:34:08 +03:00
if err != nil {
return 0 , errors . Wrap ( err , "cannot fetch schema dump" )
}
createOptions . SetSQLUp ( string ( data ) )
2019-03-18 19:40:04 +03:00
}
2020-06-16 09:00:20 +03:00
// create pure sql based migrations here
if o . flags . Changed ( "up-sql" ) {
err = createOptions . SetSQLUp ( o . upSQL )
if err != nil {
return 0 , errors . Wrap ( err , "up migration with SQL string could not be created" )
}
}
if o . flags . Changed ( "down-sql" ) {
err = createOptions . SetSQLDown ( o . downSQL )
if err != nil {
return 0 , errors . Wrap ( err , "down migration with SQL string could not be created" )
}
}
if ! o . flags . Changed ( "sql-from-file" ) && ! o . flags . Changed ( "metadata-from-file" ) && ! o . metaDataServer && ! o . sqlServer && o . EC . Config . Version != cli . V1 && ! o . flags . Changed ( "up-sql" ) && ! o . flags . Changed ( "down-sql" ) {
2020-02-25 09:46:11 +03:00
// Set empty data for [up|down].sql
createOptions . SQLUp = [ ] byte ( ` ` )
createOptions . SQLDown = [ ] byte ( ` ` )
}
2019-03-18 19:40:04 +03:00
defer func ( ) {
if err != nil {
createOptions . Delete ( )
}
} ( )
err = createOptions . Create ( )
2018-06-24 16:40:48 +03:00
if err != nil {
2019-04-03 14:29:58 +03:00
return 0 , errors . Wrap ( err , "error creating migration files" )
2018-06-24 16:40:48 +03:00
}
2021-09-22 14:47:37 +03:00
if o . fromServer {
opts := & MigrateApplyOptions {
EC : o . EC ,
SkipExecution : true ,
VersionMigration : fmt . Sprintf ( "%d" , timestamp ) ,
Source : o . Source ,
}
err := opts . Run ( )
if err != nil {
o . EC . Logger . Warnf ( "cannot mark created migration %d as applied: %v" , timestamp , err )
o . EC . Logger . Warnf ( "manually mark it as applied using command: hasura migrate apply --skip-execution --version %d" , timestamp )
}
}
2019-04-30 11:34:08 +03:00
return timestamp , nil
2018-06-24 16:40:48 +03:00
}
func getTime ( ) int64 {
startTime := time . Now ( )
return startTime . UnixNano ( ) / int64 ( time . Millisecond )
}