Skip to content

Commit

Permalink
cli: port migrate squash and migrate delete to use internal/errors
Browse files Browse the repository at this point in the history
PR-URL: hasura/graphql-engine-mono#6548
GitOrigin-RevId: 21771cb44655788be71c61b7ee3b4159341907a1
  • Loading branch information
scriptonist authored and hasura-bot committed Nov 3, 2022
1 parent b0d5238 commit e982a49
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 33 deletions.
40 changes: 24 additions & 16 deletions cli/commands/migrate_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strconv"

"github.com/hasura/graphql-engine/cli/v2"
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
"github.com/hasura/graphql-engine/cli/v2/internal/metadatautil"
"github.com/hasura/graphql-engine/cli/v2/migrate"
mig "github.com/hasura/graphql-engine/cli/v2/migrate/cmd"
Expand All @@ -28,25 +29,26 @@ func newMigrateDeleteCmd(ec *cli.ExecutionContext) *cobra.Command {
hasura migrate delete --all --database-name <database-name>`,
SilenceUsage: true,
PreRunE: func(cmd *cobra.Command, args []string) error {
op := genOpName(cmd, "PreRunE")
ec.Logger.Warn("[PREVIEW] this command is in preview. usage may change in future\n")
if err := validateConfigV3FlagsWithAll(cmd, ec); err != nil {
return err
return errors.E(op, err)
}
if !cmd.Flags().Changed("all") && !cmd.Flags().Changed("version") {
return fmt.Errorf("at least one flag [--all , --version] should be set")
return errors.E(op, fmt.Errorf("at least one flag [--all , --version] should be set"))
}
if cmd.Flags().Changed("all") && cmd.Flags().Changed("version") {
return fmt.Errorf("only one of [--all , --version] should be set")
return errors.E(op, fmt.Errorf("only one of [--all , --version] should be set"))
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {

op := genOpName(cmd, "RunE")
// exit if user inputs n for clearing migrations
if cmd.Flags().Changed("all") && !opts.force && opts.EC.IsTerminal {
confirmation, err := util.GetYesNoPrompt("clear all migrations of database and it's history on the server?")
if err != nil {
return fmt.Errorf("error getting user input: %w", err)
return errors.E(op, fmt.Errorf("error getting user input: %w", err))
}
if !confirmation {
return nil
Expand All @@ -56,15 +58,15 @@ func newMigrateDeleteCmd(ec *cli.ExecutionContext) *cobra.Command {
if ec.AllDatabases && !opts.force {
confirmation, err := util.GetYesNoPrompt("clear all mentioned migrations of all databases and it's history on the server?")
if err != nil {
return fmt.Errorf("error getting user input: %w", err)
return errors.E(op, fmt.Errorf("error getting user input: %w", err))
}
if !confirmation {
return nil
}
}

if err := opts.Run(); err != nil {
return fmt.Errorf("operation failed: %w", err)
return errors.E(op, fmt.Errorf("operation failed: %w", err))
}
return nil
},
Expand All @@ -90,37 +92,42 @@ type MigrateDeleteOptions struct {
}

func (o *MigrateDeleteOptions) Run() error {
var op errors.Op = "commands.MigrateDeleteOptions.Run"
o.EC.Spin("Removing migrations")
defer o.EC.Spinner.Stop()
if ec.AllDatabases {
sourcesAndKind, err := metadatautil.GetSourcesAndKind(o.EC.APIClient.V1Metadata.ExportMetadata)
if err != nil {
return fmt.Errorf("got error while getting the sources list : %v", err)
return errors.E(op, fmt.Errorf("got error while getting the sources list : %v", err))
}
for _, source := range sourcesAndKind {
o.Source = cli.Source(source)
err := o.RunOnSource()
if err != nil {
return fmt.Errorf("error while deleting status for database %s: %v", o.Source.Name, err)
return errors.E(op, fmt.Errorf("error while deleting status for database %s: %v", o.Source.Name, err))
}
}
return nil
}
o.Source = ec.Source
return o.RunOnSource()
if err := o.RunOnSource(); err != nil {
return errors.E(op, err)
}
return nil
}

func (o *MigrateDeleteOptions) RunOnSource() error {
var op errors.Op = "commands.MigrateDeleteOptions.RunOnSource"
o.EC.Spin("Deleting migration...")

migrateDrv, err := migrate.NewMigrate(o.EC, true, o.Source.Name, o.Source.Kind)
if err != nil {
return fmt.Errorf("error in creation of new migrate instance %w", err)
return errors.E(op, fmt.Errorf("error in creation of new migrate instance %w", err))
}

status, err := migrateDrv.GetStatus()
if err != nil {
return fmt.Errorf("error while retrieving migration status %w", err)
return errors.E(op, fmt.Errorf("error while retrieving migration status %w", err))
}

// sourceVersions migration versions in source to be deleted similarly with serverVersions
Expand All @@ -129,7 +136,7 @@ func (o *MigrateDeleteOptions) RunOnSource() error {
if !o.all {
// if o.version isn't present on source and on server return error version isn't present.
if _, ok := status.Migrations[o.version]; !ok {
return fmt.Errorf("version %v not found", o.version)
return errors.E(op, fmt.Errorf("version %v not found", o.version))
}
sourceVersions = []uint64{o.version}
serverVersions = []uint64{o.version}
Expand All @@ -147,14 +154,14 @@ func (o *MigrateDeleteOptions) RunOnSource() error {
// resets the migrations on server
err = migrateDrv.RemoveVersions(serverVersions)
if err != nil {
return fmt.Errorf("error removing migration from server: %w", err)
return errors.E(op, fmt.Errorf("error removing migration from server: %w", err))
}

// removes the migrations on source
if !o.onlyServer {
err = DeleteVersions(o.EC, sourceVersions, o.Source)
if err != nil {
return fmt.Errorf("error removing migrations from project: %w", err)
return errors.E(op, fmt.Errorf("error removing migrations from project: %w", err))
}
}
o.EC.Spinner.Stop()
Expand All @@ -163,14 +170,15 @@ func (o *MigrateDeleteOptions) RunOnSource() error {
}

func DeleteVersions(ec *cli.ExecutionContext, versions []uint64, source cli.Source) error {
var op errors.Op = "commands.DeleteVersions"
for _, v := range versions {
delOptions := mig.CreateOptions{
Version: strconv.FormatUint(v, 10),
Directory: filepath.Join(ec.MigrationDir, source.Name),
}
err := delOptions.Delete()
if err != nil {
return fmt.Errorf("unable to delete migrations from project for: %v : %w", v, err)
return errors.E(op, fmt.Errorf("unable to delete migrations from project for: %v : %w", v, err))
}
}
return nil
Expand Down
45 changes: 28 additions & 17 deletions cli/commands/migrate_squash.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import (
"strconv"
"text/tabwriter"

"github.com/hasura/graphql-engine/cli/v2/internal/errors"
"github.com/hasura/graphql-engine/cli/v2/util"

"github.com/hasura/graphql-engine/cli/v2/migrate"

"github.com/hasura/graphql-engine/cli/v2"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

Expand All @@ -37,15 +37,23 @@ func newMigrateSquashCmd(ec *cli.ExecutionContext) *cobra.Command {
hasura migrate squash --name "<name>" --from 123`,
SilenceUsage: true,
PreRunE: func(cmd *cobra.Command, args []string) error {
return validateConfigV3Flags(cmd, ec)
op := genOpName(cmd, "PreRunE")
if err := validateConfigV3Flags(cmd, ec); err != nil {
return errors.E(op, err)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
op := genOpName(cmd, "RunE")
opts.newVersion = getTime()
opts.Source = ec.Source
if opts.EC.HasMetadataV3 && opts.EC.Config.Version < cli.V2 {
return fmt.Errorf("squashing when using metadata V3 is supported from Config V2 only")
return errors.E(op, fmt.Errorf("squashing when using metadata V3 is supported from Config V2 only"))
}
if err := opts.run(); err != nil {
return errors.E(op, err)
}
return opts.run()
return nil
},
}

Expand Down Expand Up @@ -77,67 +85,68 @@ type migrateSquashOptions struct {
}

func (o *migrateSquashOptions) run() error {
var op errors.Op = "commands.migrateSquashOptions.run"
o.EC.Logger.Warnln("This command is currently experimental and hence in preview, correctness of squashed migration is not guaranteed!")
o.EC.Spin(fmt.Sprintf("Squashing migrations from %d to latest...", o.from))
defer o.EC.Spinner.Stop()
migrateDrv, err := migrate.NewMigrate(o.EC, true, o.Source.Name, o.Source.Kind)
if err != nil {
return errors.Wrap(err, "unable to initialize migrations driver")
return errors.E(op, fmt.Errorf("unable to initialize migrations driver: %w", err))
}
status, err := migrateDrv.GetStatus()
if err != nil {
return fmt.Errorf("finding status: %w", err)
return errors.E(op, fmt.Errorf("finding status: %w", err))
}
var toMigration, fromMigration *migrate.MigrationStatus
fromMigration, ok := status.Read(o.from)
if !ok {
return fmt.Errorf("validating 'from' migration failed. Make sure migration with version %v exists", o.from)
return errors.E(op, fmt.Errorf("validating 'from' migration failed. Make sure migration with version %v exists", o.from))
}
if o.to == -1 {
toMigration = status.Migrations[status.Index[status.Index.Len()-1]]
} else {
var ok bool
if int64(o.from) > o.to {
return fmt.Errorf("cannot squash from %v to %v: %v (from) should be less than %v (to)", o.from, o.to, o.from, o.to)
return errors.E(op, fmt.Errorf("cannot squash from %v to %v: %v (from) should be less than %v (to)", o.from, o.to, o.from, o.to))
}
toMigration, ok = status.Read(uint64(o.to))
if !ok {
return fmt.Errorf("validating 'to' migration failed. Make sure migration with version %v exists", o.to)
return errors.E(op, fmt.Errorf("validating 'to' migration failed. Make sure migration with version %v exists", o.to))
}
}
if err := validateMigrations(status, fromMigration.Version, toMigration.Version); err != nil {
return err
return errors.E(op, err)
}

versions, err := mig.SquashCmd(migrateDrv, o.from, o.to, o.newVersion, o.name, filepath.Join(o.EC.MigrationDir, o.Source.Name))
o.EC.Spinner.Stop()
if err != nil {
return errors.Wrap(err, "unable to squash migrations")
return errors.E(op, fmt.Errorf("unable to squash migrations: %w", err))
}

var uversions []uint64
for _, version := range versions {
if version < 0 {
return fmt.Errorf("operation failed found version value should >= 0, which is not expected")
return errors.E(op, fmt.Errorf("operation failed found version value should >= 0, which is not expected"))
}
uversions = append(uversions, uint64(version))
}

newSquashedMigrationsDestination := filepath.Join(o.EC.MigrationDir, o.Source.Name, fmt.Sprintf("squashed_%d_to_%d", uversions[0], uversions[len(uversions)-1]))
err = os.MkdirAll(newSquashedMigrationsDestination, os.ModePerm)
if err != nil {
return fmt.Errorf("creating directory to move squashed migrations: %w", err)
return errors.E(op, fmt.Errorf("creating directory to move squashed migrations: %w", err))
}

err = moveMigrations(o.EC, uversions, o.EC.Source, newSquashedMigrationsDestination)
if err != nil {
return fmt.Errorf("moving squashed migrations: %w", err)
return errors.E(op, fmt.Errorf("moving squashed migrations: %w", err))
}
oldPath := filepath.Join(o.EC.MigrationDir, o.Source.Name, fmt.Sprintf("%d_%s", o.newVersion, o.name))
newPath := filepath.Join(o.EC.MigrationDir, o.Source.Name, fmt.Sprintf("%d_%s", toMigration.Version, o.name))
err = os.Rename(oldPath, newPath)
if err != nil {
return fmt.Errorf("renaming squashed migrations: %w", err)
return errors.E(op, fmt.Errorf("renaming squashed migrations: %w", err))
}

o.EC.Logger.Infof("Created '%d_%s' after squashing '%d' till '%d'", toMigration.Version, o.name, versions[0], versions[len(versions)-1])
Expand All @@ -163,6 +172,7 @@ func (o *migrateSquashOptions) run() error {
}

func validateMigrations(status *migrate.Status, from uint64, to uint64) error {
var op errors.Op = "commands.validateMigrations"
// do not allow squashing a set of migrations when they are out of sync
// ie if I want to squash the following set of migrations
// 1
Expand All @@ -186,22 +196,23 @@ func validateMigrations(status *migrate.Status, from uint64, to uint64) error {
for idx := fromIndex + 1; idx <= toIndex; idx++ {
migration := status.Migrations[status.Index[idx]]
if !(migration.IsApplied == prevApplied && migration.IsPresent) {
return fmt.Errorf("migrations are out of sync. all migrations selected to squash should be applied or all should be be unapplied. found first mismatch at %v. use 'hasura migrate status' to inspect", migration.Version)
return errors.E(op, fmt.Errorf("migrations are out of sync. all migrations selected to squash should be applied or all should be be unapplied. found first mismatch at %v. use 'hasura migrate status' to inspect", migration.Version))
}
}

return nil
}

func moveMigrations(ec *cli.ExecutionContext, versions []uint64, source cli.Source, destination string) error {
var op errors.Op = "commands.moveMigrations"
for _, v := range versions {
moveOpts := mig.CreateOptions{
Version: strconv.FormatUint(v, 10),
Directory: filepath.Join(ec.MigrationDir, source.Name),
}
err := moveOpts.MoveToDir(destination)
if err != nil {
return fmt.Errorf("unable to move migrations from project for: %v : %w", v, err)
return errors.E(op, fmt.Errorf("unable to move migrations from project for: %v : %w", v, err))
}
}
return nil
Expand Down

0 comments on commit e982a49

Please sign in to comment.