Move logic for the status command into the state and migrations packages (#205)

Move the logic for the `pgroll status` command out of the CLI and into
the `migrations` and `state` package and add tests for it.

This makes it possible to consume migration status information from
packages using `pgroll` as a module.
This commit is contained in:
Andrew Farries 2023-11-22 12:36:54 +00:00 committed by GitHub
parent 34bbb24919
commit 763dabcf2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 132 additions and 39 deletions

View File

@ -3,7 +3,6 @@
package cmd
import (
"context"
"encoding/json"
"fmt"
@ -13,12 +12,6 @@ import (
"github.com/spf13/cobra"
)
type statusLine struct {
Schema string
Version string
Status string
}
var statusCmd = &cobra.Command{
Use: "status",
Short: "Show pgroll status",
@ -30,12 +23,12 @@ var statusCmd = &cobra.Command{
}
defer state.Close()
statusLine, err := statusForSchema(ctx, state, flags.Schema())
status, err := state.Status(ctx, flags.Schema())
if err != nil {
return err
}
statusJSON, err := json.MarshalIndent(statusLine, "", " ")
statusJSON, err := json.MarshalIndent(status, "", " ")
if err != nil {
return err
}
@ -44,33 +37,3 @@ var statusCmd = &cobra.Command{
return nil
},
}
func statusForSchema(ctx context.Context, st *state.State, schema string) (*statusLine, error) {
latestVersion, err := st.LatestVersion(ctx, schema)
if err != nil {
return nil, err
}
if latestVersion == nil {
latestVersion = new(string)
}
isActive, err := st.IsActiveMigrationPeriod(ctx, schema)
if err != nil {
return nil, err
}
var status string
if *latestVersion == "" {
status = "No migrations"
} else if isActive {
status = "In Progress"
} else {
status = "Complete"
}
return &statusLine{
Schema: schema,
Version: *latestVersion,
Status: status,
}, nil
}

View File

@ -389,6 +389,78 @@ func TestViewsAreCreatedWithSecurityInvokerTrue(t *testing.T) {
})
}
func TestStatusMethodReturnsCorrectStatus(t *testing.T) {
t.Parallel()
withMigratorAndConnectionToContainer(t, func(mig *roll.Roll, db *sql.DB) {
ctx := context.Background()
// Get the initial migration status before any migrations are run
status, err := mig.Status(ctx, "public")
assert.NoError(t, err)
// Ensure that the status shows "No migrations"
assert.Equal(t, &state.Status{
Schema: "public",
Version: "",
Status: state.NoneMigrationStatus,
}, status)
// Start a migration
err = mig.Start(ctx, &migrations.Migration{
Name: "01_create_table",
Operations: []migrations.Operation{createTableOp("table1")},
})
assert.NoError(t, err)
// Get the migration status
status, err = mig.Status(ctx, "public")
assert.NoError(t, err)
// Ensure that the status shows "In progress"
assert.Equal(t, &state.Status{
Schema: "public",
Version: "01_create_table",
Status: state.InProgressMigrationStatus,
}, status)
// Rollback the migration
err = mig.Rollback(ctx)
assert.NoError(t, err)
// Get the migration status
status, err = mig.Status(ctx, "public")
assert.NoError(t, err)
// Ensure that the status shows "No migrations"
assert.Equal(t, &state.Status{
Schema: "public",
Version: "",
Status: state.NoneMigrationStatus,
}, status)
// Start and complete a migration
err = mig.Start(ctx, &migrations.Migration{
Name: "01_create_table",
Operations: []migrations.Operation{createTableOp("table1")},
})
assert.NoError(t, err)
err = mig.Complete(ctx)
assert.NoError(t, err)
// Get the migration status
status, err = mig.Status(ctx, "public")
assert.NoError(t, err)
// Ensure that the status shows "Complete"
assert.Equal(t, &state.Status{
Schema: "public",
Version: "01_create_table",
Status: state.CompleteMigrationStatus,
}, status)
})
}
func createTableOp(tableName string) *migrations.OpCreateTable {
return &migrations.OpCreateTable{
Name: tableName,

View File

@ -75,6 +75,10 @@ func (m *Roll) PGVersion() PGVersion {
return m.pgVersion
}
func (m *Roll) Status(ctx context.Context, schema string) (*state.Status, error) {
return m.state.Status(ctx, schema)
}
func (m *Roll) Close() error {
err := m.state.Close()
if err != nil {

View File

@ -334,6 +334,37 @@ func (s *State) PreviousVersion(ctx context.Context, schema string) (*string, er
return parent, nil
}
// Status returns the current migration status of the specified schema
func (s *State) Status(ctx context.Context, schema string) (*Status, error) {
latestVersion, err := s.LatestVersion(ctx, schema)
if err != nil {
return nil, err
}
if latestVersion == nil {
latestVersion = new(string)
}
isActive, err := s.IsActiveMigrationPeriod(ctx, schema)
if err != nil {
return nil, err
}
var status MigrationStatus
if *latestVersion == "" {
status = NoneMigrationStatus
} else if isActive {
status = InProgressMigrationStatus
} else {
status = CompleteMigrationStatus
}
return &Status{
Schema: schema,
Version: *latestVersion,
Status: status,
}, nil
}
// ReadSchema reads & returns the current schema from postgres
func ReadSchema(ctx context.Context, conn *sql.DB, stateSchema, schemaname string) (*schema.Schema, error) {
var res schema.Schema

23
pkg/state/status.go Normal file
View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
package state
type MigrationStatus string
const (
NoneMigrationStatus MigrationStatus = "No migrations"
InProgressMigrationStatus MigrationStatus = "In progress"
CompleteMigrationStatus MigrationStatus = "Complete"
)
// Status describes the current migration status of a database schema.
type Status struct {
// The schema name.
Schema string `json:"schema"`
// The name of the latest version schema.
Version string `json:"version"`
// The status of the most recent migration.
Status MigrationStatus `json:"status"`
}