2021-09-29 09:14:15 +03:00
package commands
import (
"fmt"
"github.com/hasura/graphql-engine/cli/v2"
2022-11-07 19:00:39 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
2021-09-29 09:14:15 +03:00
"github.com/hasura/graphql-engine/cli/v2/internal/fsm"
"github.com/hasura/graphql-engine/cli/v2/util"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
func NewDeployCmd ( ec * cli . ExecutionContext ) * cobra . Command {
v := viper . New ( )
opts := & DeployOptions {
EC : ec ,
}
deployCmd := & cobra . Command {
Use : "deploy" ,
Short : "(PREVIEW) Utility command to apply metadata & database migrations to graphql-engine" ,
Example : `
# Apply metadata and migrations on Hasura GraphQL engine
hasura deploy
2022-12-22 10:55:37 +03:00
# Apply metadata , migrations and seeds on Hasura GraphQL engine
hasura deploy -- with - seeds
2021-09-29 09:14:15 +03:00
# Use with admin secret :
hasura deploy -- admin - secret "<admin-secret>"
# Use with endpoint :
hasura deploy -- endpoint "<endpoint>" ` ,
SilenceUsage : false ,
PersistentPreRunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-07 19:00:39 +03:00
op := genOpName ( cmd , "PersistentPreRunE" )
2021-09-29 09:14:15 +03:00
cmd . Root ( ) . PersistentPreRun ( cmd , args )
ec . Viper = v
err := ec . Prepare ( )
if err != nil {
2022-11-07 19:00:39 +03:00
return errors . E ( op , err )
2021-09-29 09:14:15 +03:00
}
if err := ec . Validate ( ) ; err != nil {
2022-11-07 19:00:39 +03:00
return errors . E ( op , err )
2021-09-29 09:14:15 +03:00
}
return nil
} ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
2022-11-07 19:00:39 +03:00
op := genOpName ( cmd , "RunE" )
if err := opts . Run ( ) ; err != nil {
return errors . E ( op , err )
}
return nil
2021-09-29 09:14:15 +03:00
} ,
}
f := deployCmd . Flags ( )
2022-12-22 10:55:37 +03:00
f . BoolVar ( & opts . WithSeeds , "with-seeds" , false , "apply available seeds data to databases" )
2021-09-29 09:14:15 +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" )
}
2021-09-29 09:14:15 +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" )
f . Bool ( "disable-interactive" , false , "disables interactive prompts (default: false)" )
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" ) )
util . BindPFlag ( v , "insecure_skip_tls_verify" , f . Lookup ( "insecure-skip-tls-verify" ) )
util . BindPFlag ( v , "certificate_authority" , f . Lookup ( "certificate-authority" ) )
util . BindPFlag ( v , "disable_interactive" , f . Lookup ( "disable-interactive" ) )
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-09-29 09:14:15 +03:00
return deployCmd
}
type DeployOptions struct {
EC * cli . ExecutionContext
2022-12-22 10:55:37 +03:00
WithSeeds bool
2021-09-29 09:14:15 +03:00
}
func ( opts * DeployOptions ) Run ( ) error {
2022-11-07 19:00:39 +03:00
var op errors . Op = "commands.DeployOptions.Run"
2021-09-29 09:14:15 +03:00
opts . EC . Config . DisableInteractive = true
context := & deployCtx {
2022-12-22 10:55:37 +03:00
ec : opts . EC ,
logger : opts . EC . Logger ,
err : nil ,
withSeeds : opts . WithSeeds ,
2021-09-29 09:14:15 +03:00
}
if opts . EC . Config . Version <= cli . V2 {
configV2FSM := newConfigV2DeployFSM ( )
if err := configV2FSM . SendEvent ( applyMigrations , context ) ; err != nil {
2022-11-07 19:00:39 +03:00
return errors . E ( op , err )
2021-09-29 09:14:15 +03:00
}
if configV2FSM . Current == failedOperation {
2022-11-07 19:00:39 +03:00
return errors . E ( op , fmt . Errorf ( "operation failed: %w" , context . err ) )
2021-09-29 09:14:15 +03:00
}
2021-12-23 18:58:53 +03:00
return nil
2021-09-29 09:14:15 +03:00
}
2021-12-23 18:58:53 +03:00
2021-09-29 09:14:15 +03:00
configV3FSM := newConfigV3DeployFSM ( )
if err := configV3FSM . SendEvent ( applyInitialMetadata , context ) ; err != nil {
2022-11-07 19:00:39 +03:00
return errors . E ( op , err )
2021-09-29 09:14:15 +03:00
}
if configV3FSM . Current == failedOperation {
2022-11-07 19:00:39 +03:00
return errors . E ( op , fmt . Errorf ( "operation failed: %w" , context . err ) )
2021-09-29 09:14:15 +03:00
}
return nil
}
type stateType = fsm . StateType
const (
applyingInitialMetadata stateType = "Applying Initial Metadata"
applyingInitialMetadataFailed stateType = "Applying Initial Metadata Failed"
applyingMetadata stateType = "Applying Metadata"
applyingMetadataFailed stateType = "Applying Metadata Failed"
applyingMigrations stateType = "Applying Migrations"
applyingMigrationsFailed stateType = "Applying Migrations Failed"
reloadingMetadata stateType = "Reloading Metadata"
reloadingMetadataFailed stateType = "Reloading Metadata Failed"
2022-12-22 10:55:37 +03:00
applyingSeeds stateType = "Applying Seeds"
applyingSeedsFailed stateType = "Applying Seeds Failed"
2021-09-29 09:14:15 +03:00
failedOperation stateType = "Operation Failed"
)
type eventType = fsm . EventType
const (
applyInitialMetadata eventType = "Apply Initial Metadata"
applyInitialMetadataFailed eventType = "Apply Initial Metadata Failed"
applyMetadata eventType = "Apply Metadata"
applyMetadataFailed eventType = "Apply Metadata Failed"
applyMigrations eventType = "Apply Migrations"
applyMigrationsFailed eventType = "Apply Migrations Failed"
reloadMetadata eventType = "Reload Metadata"
reloadMetadataFailed eventType = "Reload Metadata Failed"
2022-12-22 10:55:37 +03:00
applySeeds eventType = "Apply Seeds"
applySeedsFailed eventType = "Apply Seeds Failed"
2021-09-29 09:14:15 +03:00
failOperation eventType = "Operation Failed"
)
type deployCtx struct {
2022-12-22 10:55:37 +03:00
ec * cli . ExecutionContext
logger * logrus . Logger
err error
withSeeds bool
2021-09-29 09:14:15 +03:00
}
type applyingInitialMetadataAction struct { }
func ( a * applyingInitialMetadataAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
opts := MetadataApplyOptions {
EC : context . ec ,
}
context . logger . Debug ( applyingInitialMetadata )
if err := opts . Run ( ) ; err != nil {
context . err = err
return applyInitialMetadataFailed
}
return applyMigrations
}
type applyingInitialMetadataFailedAction struct { }
func ( a * applyingInitialMetadataFailedAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingInitialMetadataFailed )
if context . err != nil {
context . logger . Errorf ( "applying metadata failed" )
context . logger . Info ( "This can happen when metadata in your project metadata directory is malformed" )
2022-12-22 10:55:37 +03:00
context . logger . Debug ( context . err )
2021-09-29 09:14:15 +03:00
}
return failOperation
}
type applyingMigrationsAction struct { }
func ( a * applyingMigrationsAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingMigrations )
disableInteractive := context . ec . Config . DisableInteractive
defer func ( ) { context . ec . Config . DisableInteractive = disableInteractive } ( )
context . ec . Config . DisableInteractive = true
opts := MigrateApplyOptions {
2021-12-23 18:58:53 +03:00
EC : context . ec ,
2021-09-29 09:14:15 +03:00
}
2021-12-23 18:58:53 +03:00
opts . EC . AllDatabases = true
2021-09-29 09:14:15 +03:00
if err := opts . Run ( ) ; err != nil {
context . err = err
return applyMigrationsFailed
}
return applyMetadata
}
type applyingMigrationsFailedAction struct { }
func ( a * applyingMigrationsFailedAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingMigrationsFailed )
if context . err != nil {
context . logger . Errorf ( "applying migrations failed" )
2022-12-22 10:55:37 +03:00
context . logger . Debug ( context . err )
2021-09-29 09:14:15 +03:00
}
return failOperation
}
type applyingMetadataAction struct { }
func ( a * applyingMetadataAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingMetadata )
opts := MetadataApplyOptions {
EC : context . ec ,
}
2021-09-29 14:11:24 +03:00
opts . EC . Spin ( "Applying metadata..." )
2021-09-29 09:14:15 +03:00
if err := opts . Run ( ) ; err != nil {
2021-09-29 14:11:24 +03:00
opts . EC . Spinner . Stop ( )
2021-09-29 09:14:15 +03:00
context . err = err
return applyMetadataFailed
}
2021-09-29 14:11:24 +03:00
opts . EC . Spinner . Stop ( )
opts . EC . Logger . Info ( "Metadata applied" )
2021-09-29 09:14:15 +03:00
return reloadMetadata
}
type applyingMetadataFailedAction struct { }
func ( a * applyingMetadataFailedAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingMetadataFailed )
if context . err != nil {
context . logger . Errorf ( "applying metadata failed" )
2022-12-22 10:55:37 +03:00
context . logger . Debug ( context . err )
2021-09-29 09:14:15 +03:00
}
return failOperation
}
type reloadingMetadataAction struct { }
func ( a * reloadingMetadataAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( reloadingMetadata )
opts := MetadataReloadOptions {
EC : context . ec ,
}
if err := opts . runWithInfo ( ) ; err != nil {
context . err = err
return reloadMetadataFailed
}
2022-12-22 10:55:37 +03:00
return applySeeds
2021-09-29 09:14:15 +03:00
}
type reloadingMetadataFailedAction struct { }
func ( a * reloadingMetadataFailedAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( reloadingMetadataFailed )
if context . err != nil {
context . logger . Errorf ( "reloading metadata failed" )
2022-12-22 10:55:37 +03:00
context . logger . Debug ( context . err )
2021-09-29 09:14:15 +03:00
}
return failOperation
}
2022-12-22 10:55:37 +03:00
type applyingSeedsAction struct { }
func ( a * applyingSeedsAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
if context . withSeeds {
context . logger . Debug ( applyingSeeds )
opts := SeedApplyOptions {
EC : context . ec ,
Driver : getSeedDriver ( context . ec , context . ec . Config . Version ) ,
}
opts . EC . AllDatabases = true
if err := opts . Run ( ) ; err != nil {
context . err = err
return applySeedsFailed
}
}
return fsm . NoOp
}
type applyingSeedsFailedAction struct { }
func ( a * applyingSeedsFailedAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( applyingSeedsFailed )
if context . err != nil {
context . logger . Errorf ( "applying seeds failed" )
context . logger . Debug ( context . err )
}
return fsm . NoOp
}
2021-09-29 09:14:15 +03:00
type failedOperationAction struct { }
func ( a * failedOperationAction ) Execute ( ctx fsm . EventContext ) eventType {
context := ctx . ( * deployCtx )
context . logger . Debug ( failedOperation )
return fsm . NoOp
}
func newConfigV3DeployFSM ( ) * fsm . StateMachine {
type State = fsm . State
type States = fsm . States
type Events = fsm . Events
return & fsm . StateMachine {
States : States {
fsm . Default : State {
Events : Events {
applyInitialMetadata : applyingInitialMetadata ,
} ,
} ,
applyingInitialMetadata : State {
Action : & applyingInitialMetadataAction { } ,
Events : Events {
applyInitialMetadataFailed : applyingInitialMetadataFailed ,
applyMigrations : applyingMigrations ,
} ,
} ,
applyingInitialMetadataFailed : State {
Action : & applyingInitialMetadataFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
applyingMigrations : State {
Action : & applyingMigrationsAction { } ,
Events : Events {
applyMigrationsFailed : applyingMigrationsFailed ,
applyMetadata : applyingMetadata ,
} ,
} ,
applyingMigrationsFailed : State {
Action : & applyingMigrationsFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
applyingMetadata : State {
Action : & applyingMetadataAction { } ,
Events : Events {
applyMetadataFailed : applyingMetadataFailed ,
reloadMetadata : reloadingMetadata ,
} ,
} ,
applyingMetadataFailed : State {
Action : & applyingMetadataFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
reloadingMetadata : State {
Action : & reloadingMetadataAction { } ,
Events : Events {
reloadMetadataFailed : reloadingMetadataFailed ,
2022-12-22 10:55:37 +03:00
applySeeds : applyingSeeds ,
2021-09-29 09:14:15 +03:00
} ,
} ,
reloadingMetadataFailed : State {
Action : & reloadingMetadataFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
2022-12-22 10:55:37 +03:00
applyingSeeds : State {
Action : & applyingSeedsAction { } ,
Events : Events {
applySeedsFailed : applyingSeedsFailed ,
} ,
} ,
applyingSeedsFailed : State {
Action : & applyingSeedsFailedAction { } ,
} ,
2021-09-29 09:14:15 +03:00
failedOperation : State {
Action : & failedOperationAction { } ,
} ,
} ,
}
}
func newConfigV2DeployFSM ( ) * fsm . StateMachine {
type State = fsm . State
type States = fsm . States
type Events = fsm . Events
return & fsm . StateMachine {
States : States {
fsm . Default : State {
Events : Events {
applyMigrations : applyingMigrations ,
failOperation : failedOperation ,
} ,
} ,
applyingMigrations : State {
Action : & applyingMigrationsAction { } ,
Events : Events {
applyMigrationsFailed : applyingMigrationsFailed ,
applyMetadata : applyingMetadata ,
} ,
} ,
applyingMigrationsFailed : State {
Action : & applyingMigrationsFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
applyingMetadata : State {
Action : & applyingMetadataAction { } ,
Events : Events {
applyMetadataFailed : applyingMetadataFailed ,
reloadMetadata : reloadingMetadata ,
} ,
} ,
applyingMetadataFailed : State {
Action : & applyingMetadataFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
reloadingMetadata : State {
Action : & reloadingMetadataAction { } ,
Events : Events {
reloadMetadataFailed : reloadingMetadataFailed ,
2022-12-22 10:55:37 +03:00
applySeeds : applyingSeeds ,
2021-09-29 09:14:15 +03:00
} ,
} ,
reloadingMetadataFailed : State {
Action : & reloadingMetadataFailedAction { } ,
Events : Events {
failOperation : failedOperation ,
} ,
} ,
2022-12-22 10:55:37 +03:00
applyingSeeds : State {
Action : & applyingSeedsAction { } ,
Events : Events {
applySeedsFailed : applyingSeedsFailed ,
} ,
} ,
applyingSeedsFailed : State {
Action : & applyingSeedsFailedAction { } ,
} ,
2021-09-29 09:14:15 +03:00
failedOperation : State {
Action : & failedOperationAction { } ,
} ,
} ,
}
}