Skip to content

Commit

Permalink
atlasexec: add migrate down command
Browse files Browse the repository at this point in the history
  • Loading branch information
masseelch committed Mar 11, 2024
1 parent 2a44544 commit ed80e04
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 4 deletions.
61 changes: 57 additions & 4 deletions atlasexec/atlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type (
}
// TriggerType defines the type for the "trigger_type" enum field.
TriggerType string
// ExecutionOrder define how Atlas computes and executes pending migration files to the database.
// MigrateExecOrder define how Atlas computes and executes pending migration files to the database.
// See: https://atlasgo.io/versioned/apply#execution-order
MigrateExecOrder string
// DeployRunContext describes what triggered this command (e.g., GitHub Action, v1.2.3)
Expand All @@ -63,6 +63,21 @@ type (
DryRun bool
Vars Vars
}
// MigrateDownParams are the parameters for the `migrate down` command.
MigrateDownParams struct {
Env string
ConfigURL string
Context *DeployRunContext
DirURL string
URL string
RevisionsSchema string
ToVersion string
Vars Vars

// Not yet supported
// DryRun bool
// TxMode string
}
// MigrateStatusParams are the parameters for the `migrate status` command.
MigrateStatusParams struct {
Env string
Expand Down Expand Up @@ -169,9 +184,9 @@ func NewClient(workingDir, execPath string) (_ *Client, err error) {
// })
// return err
// })
func (t Client) WithWorkDir(dir string, fn func(*Client) error) error {
t.workingDir = dir
return fn(&t)
func (c *Client) WithWorkDir(dir string, fn func(*Client) error) error {
c.workingDir = dir
return fn(c)
}

// Login runs the 'login' command.
Expand Down Expand Up @@ -282,6 +297,44 @@ func (c *Client) MigrateApplySlice(ctx context.Context, params *MigrateApplyPara
return jsonDecodeErr[MigrateApply](newMigrateApplyError)(c.runCommand(ctx, args))
}

// MigrateDown runs the 'migrate down' command.
func (c *Client) MigrateDown(ctx context.Context, params *MigrateDownParams) (*MigrateDown, error) {
args := []string{"migrate", "down", "--format", "{{ json . }}"}
if params.Env != "" {
args = append(args, "--env", params.Env)
}
if params.ConfigURL != "" {
args = append(args, "--config", params.ConfigURL)
}
if params.Context != nil {
buf, err := json.Marshal(params.Context)
if err != nil {
return nil, err
}
args = append(args, "--context", string(buf))
}
if params.URL != "" {
args = append(args, "--url", params.URL)
}
if params.DirURL != "" {
args = append(args, "--dir", params.DirURL)
}
if params.RevisionsSchema != "" {
args = append(args, "--revisions-schema", params.RevisionsSchema)
}
if params.ToVersion != "" {
args = append(args, "--to-version", params.ToVersion)
}
args = append(args, params.Vars.AsArgs()...)
r, err := c.runCommand(ctx, args)
if cliErr := (cliError{}); errors.As(err, &cliErr) && cliErr.stderr == "" {
r = strings.NewReader(cliErr.stdout)
err = nil
}
// NOTE: This command only support one result.
return firstResult(jsonDecode[MigrateDown](r, err))
}

// SchemaApply runs the 'schema apply' command.
func (c *Client) SchemaApply(ctx context.Context, params *SchemaApplyParams) (*SchemaApply, error) {
return firstResult(c.SchemaApplySlice(ctx, params))
Expand Down
25 changes: 25 additions & 0 deletions atlasexec/atlas_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ type (
// but by Atlas, e.g. when committing or rolling back a transaction.
Error string `json:"Error,omitempty"`
}
// MigrateDown contains a summary of a migration down attempt on a database.
MigrateDown struct {
Planned []File `json:"Planned,omitempty"` // Pending migration files
Applied []*AppliedFile `json:"Applied,omitempty"` // Applied files
Current string `json:"Current,omitempty"` // Current migration version
Target string `json:"Target,omitempty"` // Target migration version
Start time.Time
End time.Time
// Error is set even then, if it was not caused by a statement in a migration file,
// but by Atlas, e.g. when committing or rolling back a transaction.
Error string `json:"Error,omitempty"`
}
// MigrateStatus contains a summary of the migration status of a database.
MigrateStatus struct {
Available []File `json:"Available,omitempty"` // Available migration files
Expand Down Expand Up @@ -142,6 +154,11 @@ type (
MigrateApplyError struct {
Result []*MigrateApply
}
// MigrateDownError is returned when an error occurred
// during a migration down attempt.
MigrateDownError struct {
Result []*MigrateDown
}
// SchemaApplyError is returned when an error occurred
// during a schema applying attempt.
SchemaApplyError struct {
Expand All @@ -152,6 +169,9 @@ type (
// Error implements the error interface.
func (e *MigrateApplyError) Error() string { return last(e.Result).Error }

// Error implements the error interface.
func (e *MigrateDownError) Error() string { return last(e.Result).Error }

// Error implements the error interface.
func (e *SchemaApplyError) Error() string { return last(e.Result).Error }

Expand All @@ -169,6 +189,11 @@ func (r *SummaryReport) DiagnosticsCount() int {
func newMigrateApplyError(r []*MigrateApply) error {
return &MigrateApplyError{Result: r}
}

func newMigrateDownError(r []*MigrateDown) error {

Check failure on line 193 in atlasexec/atlas_models.go

View workflow job for this annotation

GitHub Actions / golangci-lint

func `newMigrateDownError` is unused (unused)
return &MigrateDownError{Result: r}
}

func newSchemaApplyError(r []*SchemaApply) error {
return &SchemaApplyError{Result: r}
}
Expand Down
51 changes: 51 additions & 0 deletions atlasexec/atlas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -841,3 +841,54 @@ func TestMigrateApply(t *testing.T) {
})
}
}

func TestMigrateDown(t *testing.T) {
wd, err := os.Getwd()
require.NoError(t, err)
// Mock the client with a script that just prints the arguments to stderr and
// exit with an error code.
c, err := atlasexec.NewClient(t.TempDir(), filepath.Join(wd, "./mock-args.sh"))
require.NoError(t, err)

for _, tt := range []struct {
name string
params *atlasexec.MigrateDownParams
expect string
}{
{
name: "no params",
params: &atlasexec.MigrateDownParams{},
expect: "migrate down --format {{ json . }}",
},
{
name: "with env",
params: &atlasexec.MigrateDownParams{
Env: "test",
},
expect: "migrate down --format {{ json . }} --env test",
},
{
name: "with url",
params: &atlasexec.MigrateDownParams{
URL: "sqlite://file?_fk=1&cache=shared&mode=memory",
},
expect: "migrate down --format {{ json . }} --url sqlite://file?_fk=1&cache=shared&mode=memory",
},
{
name: "with target version",
params: &atlasexec.MigrateDownParams{
ToVersion: "12345",
},
expect: "migrate down --format {{ json . }} --to-version 12345",
},
} {
t.Run(tt.name, func(t *testing.T) {
_, err := c.MigrateDown(context.Background(), tt.params)
require.Error(t, err)
// The script mock-args.sh exit with an error code.
// So, our atlasexec.MigrateApply should return a cliError.
// Which contains all output from the script (both stdout and stderr).
require.Equal(t, tt.expect, err.Error())
})
}
}

0 comments on commit ed80e04

Please sign in to comment.