migrations

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2021 License: Apache-2.0 Imports: 6 Imported by: 5

README

migrations

Migrations is an abstraction for a migration system. It can migrate anything.

How to use

TODO

Extending

To get it working the system as divided into 4 main components which 2 are interfaces and 2 are concrete implementations:

PS: The examples here will be database related. But, this library is able to migrate anything that migrations would apply.

1. Source

In some migration systems (like golang-migrate/migrate) are stored as .sql files and are stored into a directory as <timestamp>_<description>.(up|down).sql. In other systems, the migration is a func that need to do some complex work and should connect many components before the actual database migration.

So, in the first example, the Source is a directory containing a bunch of .sql files with specific names. In the second example, the Source are function that should be organized chronologically.

Hence, Source is the media that persists the migrations themselves. In practice, it is just an interface{} with a bunch of methods that will list all available migrations (check the code).

TODO: Link the Source interface.

2. Target

A Target are what the migrations are transforming. If you are dealing with relational databases, like postgres, you would use a TargetSQL implementation (we provide one, check the our examples folder|TODO).

In practice, a Target is just an interface{} with a bunch of methods that will list executed migrations, mark and unmark migrations as executed (check the code).

TODO: Link the Target interface.

3. Executer

An Executer integrations Source and Target and is responsible for step actions, like Do and Undo. Each call will step forward or backward one migration at a time.

4. Runner

Runners are, also, concrete. They capture the developer intentions and call the Executer.

Let's say that you want to migrate your system. By that, you mean to run all pending migrations. So the runner will use the Executer.Do calling it multiple times to get all migrations executed.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNonUniqueMigrationID       = errors.New("migration id is not unique")
	ErrMigrationNotFound          = errors.New("migration not found")
	ErrNoCurrentMigration         = errors.New("no current migration")
	ErrCurrentMigrationNotFound   = errors.New("current migration not found in the list")
	ErrCurrentMigrationMoreRecent = errors.New("current migration is more recent than target migration")
	ErrNoMigrationsAvailable      = errors.New("no migrations available")
	ErrMigrationNotUndoable       = errors.New("migration cannot be undone")

	// ErrStepOutOfIndex is returned when a `StepResolver` cannot resolve a
	// migration due to the resolved index be outside of the migration list.
	ErrStepOutOfIndex = errors.New("step out of bounds")

	// ErrMigrationNotListed is returned when a migration is not found in the
	// `Source` list.
	ErrMigrationNotListed = errors.New("migration not in the source list")

	// ErrStaleMigrationDetected is returned when a migration with an ID eariler of the current applied migration is
	// detected.
	ErrStaleMigrationDetected = errors.New("stale migration detected")

	// ErrInvalidAction is returned when, while executing, the `Action.Action`
	// has an invalid value.
	ErrInvalidAction = errors.New("undefined action")
)
View Source
var (
	ErrInvalidFilename    = errors.New("invalid filename")
	ErrInvalidMigrationID = errors.New("invalid migration ID")
)
View Source
var (
	// DefaultMigrationIDFormat is the default format for the migrations ID.
	DefaultMigrationIDFormat = "20060102150405"
)
View Source
var DefaultSource = NewSource()

DefaultSource is the default source instance.

View Source
var (
	MigrationIDNone = time.Unix(0, 0)
)

Functions

func NewQueryError

func NewQueryError(err error, query string) error

Types

type Action

type Action struct {
	Action    ActionType
	Migration Migration
}

type ActionType

type ActionType string
const (
	ActionTypeDo   ActionType = "do"
	ActionTypeUndo ActionType = "undo"
)

type BaseMigration

type BaseMigration struct {
	// contains filtered or unexported fields
}

func NewMigration

func NewMigration(id, description string, do, undo migrationFunc) *BaseMigration

func (*BaseMigration) CanUndo

func (migration *BaseMigration) CanUndo() bool

CanUndo is a flag that mark this flag as undoable.

func (*BaseMigration) Description

func (migration *BaseMigration) Description() string

Description is the humanized description for the migration.

func (*BaseMigration) Do

func (migration *BaseMigration) Do(ctx context.Context) error

Do will execute the migration.

func (*BaseMigration) ID

func (migration *BaseMigration) ID() string

ID identifies the migration. Through the ID, all the sorting is done.

func (*BaseMigration) Next

func (migration *BaseMigration) Next() Migration

Next will link this migration with the next. This link should be created by the source while it is being loaded.

func (*BaseMigration) Previous

func (migration *BaseMigration) Previous() Migration

Previous will link this migration with the previous. This link should be created by the Source while it is being loaded.

func (*BaseMigration) SetNext

func (migration *BaseMigration) SetNext(value Migration) Migration

SetNext will set the next migration

func (*BaseMigration) SetPrevious

func (migration *BaseMigration) SetPrevious(value Migration) Migration

SetPrevious will set the previous migration

func (*BaseMigration) String

func (migration *BaseMigration) String() string

String will return a representation of the migration into a string format for user identification.

func (*BaseMigration) Undo

func (migration *BaseMigration) Undo(ctx context.Context) error

Undo will undo the migration.

type ExecutionStats

type ExecutionStats struct {
	Successful []*Action
	Errored    []*Action
}

func Migrate

func Migrate(ctx context.Context, runner *Runner, runnerReporter RunnerReporter) (*ExecutionStats, error)

type ListSource

type ListSource struct {
	// contains filtered or unexported fields
}

ListSource is the default Source implementation that keep all the migrations on a list.

This source is meant to be used as base implementation for other specific Sources. Check out the sql.Source implementation.

func NewSource

func NewSource() *ListSource

NewSource returns a new instance of the default implementation of the Source.

func (*ListSource) Add

func (source *ListSource) Add(migration Migration) error

Add adds a Migration to this Source. It will

func (*ListSource) ByID

func (source *ListSource) ByID(id string) (Migration, error)

func (*ListSource) List

func (source *ListSource) List() ([]Migration, error)

type Migration

type Migration interface {
	// ID identifies the migration. Through the ID, all the sorting is done.
	ID() string

	// String will return a representation of the migration into a string format
	// for user identification.
	String() string

	// Description is the humanized description for the migration.
	Description() string

	// Next will link this migration with the next. This link should be created
	// by the source while it is being loaded.
	Next() Migration

	// SetNext will set the next migration
	SetNext(Migration) Migration

	// Previous will link this migration with the previous. This link should be
	// created by the Source while it is being loaded.
	Previous() Migration

	// SetPrevious will set the previous migration
	SetPrevious(Migration) Migration

	// Do will execute the migration.
	Do(ctx context.Context) error

	// CanUndo is a flag that mark this flag as undoable.
	CanUndo() bool

	// Undo will undo the migration.
	Undo(ctx context.Context) error
}

Migration is the abstraction that defines the migration contract.

Migrations, by default, cannot be undone. But, if the final migration implement the `MigrationUndoable` interface, the system will let it be undone.

type MigrationError

type MigrationError interface {
	error
	Migration() Migration
	Unwrap() error
}

MigrationError wraps an error with a migration property.

func WrapMigration

func WrapMigration(err error, migration Migration) MigrationError

WrapMigration creates a `MigrationError` based on an existing error.

type MigrationIDError

type MigrationIDError interface {
	error
	MigrationID() string
	Unwrap() error
}

MigrationCodeError wraps an error with a migration ID.

func WrapMigrationID

func WrapMigrationID(err error, migrationID string) MigrationIDError

WrapMigrationID creates a `MigrationCodeError` based on an existing error.

type MigrationsError

type MigrationsError interface {
	error
	Migrations() []Migration
}

MigrationsError wraps an error with a list of migrations.

func WrapMigrations

func WrapMigrations(err error, migrations ...Migration) MigrationsError

type Plan

type Plan []*Action

type Planner

type Planner interface {
	Plan() (Plan, error)
}

func DoPlanner

func DoPlanner(source Source, target Target) Planner

func MigratePlanner

func MigratePlanner(source Source, target Target) Planner

func ResetPlanner

func ResetPlanner(source Source, target Target) Planner

func RewindPlanner

func RewindPlanner(source Source, target Target) Planner

func UndoPlanner

func UndoPlanner(source Source, target Target) Planner

type PlannerFunc

type PlannerFunc func(Source, Target) Planner

func StepPlanner

func StepPlanner(step int) PlannerFunc

type ProgressReporter

type ProgressReporter interface {
	SetStep(current int)
	SetSteps(steps []string)
	SetTotal(total int)
	SetProgress(progress int)
}

type QueryError

type QueryError interface {
	Query() string
}

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner will receive the `Plan` from the `Planner` and execute it.

func NewRunner

func NewRunner(source Source, target Target) *Runner

func (*Runner) Execute

func (runner *Runner) Execute(ctx context.Context, plan Plan, reporter RunnerReporter) (*ExecutionStats, error)

Execute performs a plan, running all actions migration by migration.

Before running, Execute will check for Undo actions that cannot be performmed into undoable migrations. If that happens, an `ErrMigrationNotUndoable` will be returned and nothing will be executed.

For each migration executed, the system will move the cursor to that point. So that, if any error happens during the migration execution (do or undo), the execution will be stopped and the error will be returned. All performed actions WILL NOT be rolled back.

type RunnerOption

type RunnerOption func(*Runner)

type RunnerReporter

type RunnerReporter interface {
	BeforeExecute(ctx context.Context, plan Plan)
	BeforeExecuteMigration(ctx context.Context, actionType ActionType, migration Migration)
	AfterExecuteMigration(ctx context.Context, actionType ActionType, migration Migration, err error)
	AfterExecute(ctx context.Context, plan Plan, stats *ExecutionStats, err error)
}

type Source

type Source interface {
	// ByID will return the Migration reference given the ID.
	//
	// If the migration id cannot be found, this function should return ErrMigrationNotFound.
	ByID(string) (Migration, error)

	// List will return the list of available migrations, sorted by ID (the older first, the newest last).
	//
	// If there is no migrations available, an ErrNoMigrationsAvailable should
	// be returned.
	List() ([]Migration, error)
}

Source is responsible to list all migrations available to run.

Migrations can be stored into many medias, from Go source code files, plain SQL files, go:embed. So, this interface is responsible for abstracting how this system accepts any media to list the

type Target

type Target interface {
	// Current returns the reference to the most recent migration applied to the system.
	//
	// If there is no migration run, the system will return an ErrNoCurrentMigration error.
	Current() (Migration, error)

	// Create ensures the creation of the list of migrations is done successfully. As an example, if this was an SQL
	// database implementation, this method would create the `_migrations` table.
	Create() error

	// Destroy removes the list of the applied migrations. As an example, if this was an SQL database implementation,
	// this would drop the `_migrations` table.
	Destroy() error

	// Done list all the migrations that were successfully applied.
	Done() ([]Migration, error)

	// Add adds a migration to the list of successful migrations.
	Add(Migration) error

	// Remove removes a migration from the list of successful migrations.
	Remove(Migration) error
}

Target is responsible for managing the state of the migration system. This interface abstracts the operations needed to list what migrations were successfully executed, what is the current migration, etc.

type TargetLocker added in v0.2.0

type TargetLocker interface {
	// Lock will try locking the migration system in such way no other instance of the process can run the migrations.
	Lock() (Unlocker, error)
}

TargetLocker abstracts the locking mechanism for specific target implementations.

type Unlocker added in v0.2.0

type Unlocker interface {
	Unlock() error
}

Unlocker abstracts an implementation for unlocking the migration system.

Directories

Path Synopsis
cmd
internal
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL