Skip to content

Commit

Permalink
feat: add migrate sql up|down|status
Browse files Browse the repository at this point in the history
This patch adds the ability to execute down migrations using:

```
kratos migrate sql down -e --steps {num_of_steps}
```

Please read `kratos migrate sql down --help` carefully.

Going forward, please use the following commands

```
kratos migrate sql up ...
kratos migrate sql status ...
```

instead of the previous, now deprecated

```
kratos migrate sql ...
kratos migrate status ...
```

commands.

See ory-corp/cloud#7350
  • Loading branch information
aeneasr committed Nov 27, 2024
1 parent e6d2d4d commit d618193
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 138 deletions.
73 changes: 24 additions & 49 deletions cmd/cliclient/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
package cliclient

import (
"bufio"
"bytes"
"fmt"
"os"
"strings"

"github.com/ory/x/popx"
"github.com/ory/x/servicelocatorx"

"github.com/pkg/errors"
Expand All @@ -32,10 +29,7 @@ func NewMigrateHandler() *MigrateHandler {
return &MigrateHandler{}
}

func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {
var d driver.Registry
var err error

func (h *MigrateHandler) getPersister(cmd *cobra.Command, args []string, opts []driver.RegistryOption) (d driver.Registry, err error) {
if flagx.MustGetBool(cmd, "read-from-env") {
d, err = driver.NewWithoutInit(
cmd.Context(),
Expand All @@ -47,21 +41,18 @@ func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...d
configx.SkipValidation(),
})
if err != nil {
return err
return nil, err
}
if len(d.Config().DSN(cmd.Context())) == 0 {
fmt.Println(cmd.UsageString())
fmt.Println("")
fmt.Println("When using flag -e, environment variable DSN must be set")
return cmdx.FailSilently(cmd)
}
if err != nil {
return err
return nil, cmdx.FailSilently(cmd)
}
} else {
if len(args) != 1 {
fmt.Println(cmd.UsageString())
return cmdx.FailSilently(cmd)
return nil, cmdx.FailSilently(cmd)
}
d, err = driver.NewWithoutInit(
cmd.Context(),
Expand All @@ -74,54 +65,38 @@ func (h *MigrateHandler) MigrateSQL(cmd *cobra.Command, args []string, opts ...d
configx.WithValue(config.ViperKeyDSN, args[0]),
})
if err != nil {
return err
return nil, err
}
}

err = d.Init(cmd.Context(), &contextx.Default{}, append(opts, driver.SkipNetworkInit)...)
if err != nil {
return errors.Wrap(err, "an error occurred initializing migrations")
return nil, errors.Wrap(err, "an error occurred initializing migrations")
}

var plan bytes.Buffer
_, err = d.Persister().MigrationStatus(cmd.Context())
if err != nil {
return errors.Wrap(err, "an error occurred planning migrations:")
}
return d, nil
}

if !flagx.MustGetBool(cmd, "yes") {
fmt.Println("The following migration is planned:")
fmt.Println("")
fmt.Printf("%s", plan.String())
fmt.Println("")
fmt.Println("To skip the next question use flag --yes (at your own risk).")
if !askForConfirmation("Do you wish to execute this migration plan?") {
fmt.Println("Migration aborted.")
return cmdx.FailSilently(cmd)
}
func (h *MigrateHandler) MigrateSQLDown(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {
p, err := h.getPersister(cmd, args, opts)
if err != nil {
return err
}
return popx.MigrateSQLDown(cmd, p.Persister())
}

if err = d.Persister().MigrateUp(cmd.Context()); err != nil {
func (h *MigrateHandler) MigrateSQLStatus(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {
p, err := h.getPersister(cmd, args, opts)
if err != nil {
return err
}
fmt.Println("Successfully applied SQL migrations!")
return nil
return popx.MigrateStatus(cmd, p.Persister())
}

func askForConfirmation(s string) bool {
reader := bufio.NewReader(os.Stdin)

for {
fmt.Printf("%s [y/n]: ", s)

response, err := reader.ReadString('\n')
cmdx.Must(err, "%s", err)

response = strings.ToLower(strings.TrimSpace(response))
if response == "y" || response == "yes" {
return true
} else if response == "n" || response == "no" {
return false
}
func (h *MigrateHandler) MigrateSQLUp(cmd *cobra.Command, args []string, opts ...driver.RegistryOption) error {
p, err := h.getPersister(cmd, args, opts)
if err != nil {
return err
}
return popx.MigrateSQLUp(cmd, p.Persister())
}
28 changes: 27 additions & 1 deletion cmd/migrate/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ package migrate

import (
"github.com/spf13/cobra"

"github.com/ory/kratos/cmd/cliclient"
"github.com/ory/kratos/driver"
"github.com/ory/x/configx"
"github.com/ory/x/popx"
)

func NewMigrateCmd() *cobra.Command {
Expand All @@ -16,6 +21,27 @@ func NewMigrateCmd() *cobra.Command {

func RegisterCommandRecursive(parent *cobra.Command) {
c := NewMigrateCmd()
parent.AddCommand(c)

configx.RegisterFlags(c.PersistentFlags())
c.AddCommand(NewMigrateSQLCmd())

parent.AddCommand(c)
}

func NewMigrateSQLDownCmd(opts ...driver.RegistryOption) *cobra.Command {
return popx.NewMigrateSQLDownCmd("kratos", func(cmd *cobra.Command, args []string) error {
return cliclient.NewMigrateHandler().MigrateSQLDown(cmd, args, opts...)
})
}

func NewMigrateSQLUpCmd(opts ...driver.RegistryOption) *cobra.Command {
return popx.NewMigrateSQLUpCmd("kratos", func(cmd *cobra.Command, args []string) error {
return cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...)
})
}

func NewMigrateSQLStatusCmd(opts ...driver.RegistryOption) *cobra.Command {
return popx.NewMigrateSQLStatusCmd("kratos", func(cmd *cobra.Command, args []string) error {
return cliclient.NewMigrateHandler().MigrateSQLStatus(cmd, args, opts...)
})
}
14 changes: 10 additions & 4 deletions cmd/migrate/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (
// migrateSqlCmd represents the sql command
func NewMigrateSQLCmd(opts ...driver.RegistryOption) *cobra.Command {
c := &cobra.Command{
Use: "sql <database-url>",
Short: "Create SQL schemas and apply migration plans",
Use: "sql <database-url>",
Deprecated: "Please use `hydra migrate sql` instead.",
Short: "Create SQL schemas and apply migration plans",
Long: `Run this command on a fresh SQL installation and when you upgrade Ory Kratos to a new minor version.
It is recommended to run this command close to the SQL instance (e.g. same subnet) instead of over the public internet.
Expand All @@ -30,12 +31,17 @@ You can read in the database URL using the -e flag, for example:
Before running this command on an existing database, create a back up!
`,
RunE: func(cmd *cobra.Command, args []string) error {
return cliclient.NewMigrateHandler().MigrateSQL(cmd, args, opts...)
return cliclient.NewMigrateHandler().MigrateSQLUp(cmd, args, opts...)
},
}

configx.RegisterFlags(c.PersistentFlags())
c.Flags().BoolP("read-from-env", "e", false, "If set, reads the database connection string from the environment variable DSN or config file key dsn.")
c.PersistentFlags().BoolP("read-from-env", "e", false, "If set, reads the database connection string from the environment variable DSN or config file key dsn.")
c.Flags().BoolP("yes", "y", false, "If set all confirmation requests are accepted without user interaction.")

c.AddCommand(NewMigrateSQLStatusCmd(opts...))
c.AddCommand(NewMigrateSQLUpCmd(opts...))
c.AddCommand(NewMigrateSQLDownCmd(opts...))

return c
}
56 changes: 28 additions & 28 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ require (
github.com/ory/jsonschema/v3 v3.0.8
github.com/ory/mail/v3 v3.0.0
github.com/ory/nosurf v1.2.7
github.com/ory/x v0.0.669
github.com/ory/x v0.0.674
github.com/peterhellberg/link v1.2.0
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
github.com/pkg/errors v0.9.1
Expand All @@ -87,17 +87,17 @@ require (
github.com/tidwall/sjson v1.2.5
github.com/urfave/negroni v1.0.0
github.com/zmb3/spotify/v2 v2.4.2
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0
go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/sdk v1.28.0
go.opentelemetry.io/otel/trace v1.28.0
golang.org/x/crypto v0.25.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/sdk v1.32.0
go.opentelemetry.io/otel/trace v1.32.0
golang.org/x/crypto v0.28.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/net v0.27.0
golang.org/x/oauth2 v0.21.0
golang.org/x/sync v0.7.0
golang.org/x/text v0.16.0
google.golang.org/grpc v1.65.0
golang.org/x/net v0.30.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.9.0
golang.org/x/text v0.20.0
google.golang.org/grpc v1.67.1
)

require github.com/wI2L/jsondiff v0.6.0
Expand All @@ -111,7 +111,7 @@ require (
github.com/cortesi/termlog v0.0.0-20210222042314-a1eec763abec // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/term v0.25.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
mvdan.cc/sh/v3 v3.6.0 // indirect
)
Expand Down Expand Up @@ -193,7 +193,7 @@ require (
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
Expand Down Expand Up @@ -256,7 +256,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.1.14 // indirect
github.com/openzipkin/zipkin-go v0.4.2 // indirect
github.com/openzipkin/zipkin-go v0.4.3 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect
Expand All @@ -266,7 +266,7 @@ require (
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect
Expand Down Expand Up @@ -294,24 +294,24 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.47.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.21.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.15.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.57.0 // indirect
go.opentelemetry.io/contrib/propagators/b3 v1.32.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.32.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.26.0 // indirect
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect; / indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect; / indirect
go.opentelemetry.io/otel/exporters/zipkin v1.21.0 // indirect; / indirect
go.opentelemetry.io/otel/metric v1.28.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect; / indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 // indirect; / indirect
go.opentelemetry.io/otel/exporters/zipkin v1.32.0 // indirect; / indirect
go.opentelemetry.io/otel/metric v1.32.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/protobuf v1.34.2
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
google.golang.org/protobuf v1.35.1
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
Expand Down
Loading

0 comments on commit d618193

Please sign in to comment.