arangom

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Mar 1, 2023 License: MIT Imports: 10 Imported by: 0

README

arangom

GoDoc Go Report Card Maintainability Test Coverage

arangom is an ArangoDB migration tool written in Go. It is heavily inspired by ArangoMiGo which is already a great tool.

Installation

As a CLI tool

Using brew

$ brew tap gabor-boros/brew
$ brew install arangom

Manual install

To install arangom, use one of the release artifacts. If you have go installed, you can build from source as well.

As a package
go get github.com/gabor-boros/arangom

Migrations

Migrations are a set of operations that are executed in a given order. The operations are executed in the order they are defined in the migration.

When a migration is executed, depending on the status of the operations, the migration is marked as missing, running, done or failed. If a migration is marked as missing or running, that means that the migration is not executed yet, therefore those migrations will be executed. If a migration is marked as done, it is skipped. In the case of a failed migration, the migration is skipped along with all the migrations that follow it and an error is returned.

The lifecycle of a migration is the following:

  1. The migration is marked as missing.
  2. When executing the migration, the migration is marked as running.
  3. When the migration is marked as done or failed based on the result of the operations.

Every migration has a checksum that is calculated based on a unique ID and the operations of the migration. The checksum is used to determine if a migration has changed since the last execution. If the checksum of a migration has changed since the last execution, the migration is marked as missing and will be executed again.

Migration files

Migrations are stored in YAML files and are executed in alphabetical order. The files are expected to be in the following schema:

id: <numerical ID> # unique identifier of the migration, only used for checksum
operations: # list of operations to execute in the given order
  - kind: <operation kind> # the kind of the operation
    collection: <collection name> # the name of the collection to operate on
    options: <options> # the options of the operation
  # ...

Example:

id: 1677564649
operations:
  - kind: createCollection
    collection: mycollection
    options:
      waitForSync: true
      cacheEnabled: true
      schema:
        level: moderate
        message: The document does not match the schema.
        rule:
          properties:
            kind:
              type: string
              maximum: 12
          additionalProperties:
            type: string
          required:
            - kind

The files are expected to be in a migrations directory in the current working directory, but this can be changed with the -migration-dir command line flag.

The migration directory can be structured in subdirectories as desired, arangom will recursively search for migration files.

Example usage

As a package
package main

import (
	"context"
	"os"

	arangoDriver "github.com/arangodb/go-driver"
	arangoHTTP "github.com/arangodb/go-driver/http"

	arangoMigrate "github.com/gabor-boros/arangom"
)

func main() {
	conn, _ := arangoHTTP.NewConnection(arangoHTTP.ConnectionConfig{
		Endpoints: []string{os.Getenv("ARANGO_URL")},
	})

	client, _ := arangoDriver.NewClient(arangoDriver.ClientConfig{
		Connection:     conn,
		Authentication: arangoDriver.BasicAuthentication(os.Getenv("ARANGO_USER"), os.Getenv("ARANGO_PASSWORD")),
	})

	db, _ := client.Database(context.Background(), os.Getenv("ARANGO_DB"))

	migrations := []*arangom.Migration{
		{
			Path:       "0001.initial", // any unique identifier
			Operations: []arangom.Operation{
				{
					Kind:       arangom.OperationKindCollectionCreate,
					Collection: os.Getenv("ARANGO_MIGRATION_COLLECTION"),
					Options:    map[string]any{
						"waitForSync": true,
					},
				},
			},
		},
	}

	executor, _ := arangom.NewExecutor(
		arangom.WithDatabase(db),
		arangom.WithCollection(os.Getenv("ARANGO_MIGRATION_COLLECTION")),
		arangom.WithMigrations(migrations),
	)

	if err := executor.Execute(context.Background()); err != nil {
		panic(err)
	}
}
As a CLI tool
$ arangom -username "root" -password "openSesame" -database "mydb" -migration-dir "migrations"
2023/02/28 06:47:43 [INFO] connecting to the migration collection "migrations"
2023/02/28 06:47:43 [INFO] [1677564649] fetching migration status
2023/02/28 06:47:43 [INFO] [1677564649] executing migration
2023/02/28 06:47:43 [INFO] [1677564649] migration executed successfully
2023/02/28 06:47:43 [INFO] [1677564650] fetching migration status
2023/02/28 06:47:43 [INFO] [1677564650] executing migration
2023/02/28 06:47:43 [INFO] [1677564650] migration executed successfully
2023/02/28 06:47:43 [INFO] all migrations executed successfully
Flags
Usage of arangom:
  -collection string
        Migration collection (default "migrations")
  -create-collection
        Create migration collection if it does not exist (default true)
  -database string
        Database name
  -endpoints string
        Comma-separated list of database endpoints (default "http://localhost:8529")
  -migration-dir string
        Migration directory (default "migrations")
  -password string
        Database password
  -username string
        Database user
  -version
        Print version and exit

Supported operations

The options of the operations are the same as the request body options of the corresponding ArangoDB operations. However, some operations have additional options that are not supported by ArangoDB. These options are documented in the table below with the caveat column. For more information about operation options, please refer to the ArangoDB documentation.

Operation kind Description Caveats
executeAQL Executes an AQL query. executeAQL
createCollection Creates a new collection. -
updateCollection Updates an existing collection. -
deleteCollection Deletes an existing collection. -
createGraph Creates a new graph. createGraph
addVertexToGraph Adds a vertex collection to a graph. addVertexToGraph
removeVertexFromGraph Removes a vertex collection from a graph. removeVertexFromGraph
addEdgeToGraph Adds an edge definition to a graph. addEdgeToGraph
removeEdgeFromGraph Removes an edge definition from a graph. removeEdgeFromGraph
deleteGraph Deletes an existing graph. deleteGraph
createView Creates a new search view. createView
updateView Updates an existing search view. updateView
deleteView Deletes an existing search view. deleteView
createFulltextIndex Creates a fulltext index. createFulltextIndex
createGeoSpatialIndex Creates a Geo-spatial index. createGeoSpatialIndex
createHashIndex Creates a hash index. createHashIndex
createInvertedIndex Creates an inverted index. -
createPersistentIndex Creates a persistent index. createPersistentIndex
createSkipListIndex Creates a skiplist index. createSkipListIndex
createTTLIndex Creates a TTL index. createTTLIndex
createZKDIndex Creates a ZKD index. createZKDIndex
deleteIndex Deletes an index. deleteIndex

Database operations are intentionally not supported. To apply migrations on a database, create the database first. Feature requests and/or pull requests are welcomed for other missing operations.

Operation option caveats
executeAQL options

Executing AQL queries has no options defined by ArangoDB, though it is possible to bind variables to the query. The executeAQL operation therefore has the following options:

Option name Description
query The AQL query to execute.
bindVars The bind variables of the query.
createGraph options

The graph name is defined in the collection field of the operation.

addVertexToGraph options

The graph name is defined in the collection field of the operation. The options of the operation is extended with the following options:

Option name Description
collection The name of the vertex collection to add to the graph.
removeVertexFromGraph options

The graph name is defined in the collection field of the operation. The options of the operation is extended with the following options:

Option name Description
collection The name of the vertex collection to remove from the graph.
addEdgeToGraph options

The graph name is defined in the collection field of the operation. The options of the operation is extended with the following options:

Option name Description
collection The name of the edge definition to add to the graph.
constraints The edge constraints of the edge definition.

The edge constraints are defined as follows:

Option name Description
from The list of name of the vertex collections that the edge definition starts from.
to The list of name of the vertex collections that the edge definition ends to.
removeEdgeFromGraph options

The graph name is defined in the collection field of the operation. The options of the operation is extended with the following options:

Option name Description
collection The name of the edge definition to add to the graph.
deleteGraph options

The graph name is defined in the collection field of the operation.

createView options

The view name is defined in the collection field of the operation.

updateView options

The view name is defined in the collection field of the operation.

deleteView options
createFulltextIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
createGeoSpatialIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
createHashIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
createPersistentIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
createSkipListIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
createTTLIndex options

The options of the operation is extended with the following options:

Option name Description
field The field to index.
expireAfter The expiration time in seconds.
createZKDIndex options

The options of the operation is extended with the following options:

Option name Description
fields The list of fields to index.
deleteIndex options

The options of the operation is extended with the following options:

Option name Description
name The name of the index to delete.

Compatibility

The compatibility of arangom is equal to the compatibility of the ArangoDB Go driver. For more information, please refer to the ArangoDB Go driver documentation.

Q&A

Why not contributing to ArangoMiGo instead?

ArangoMiGo is a great tool, but it is highly opinionated and has design decisions that are not flexible enough. Refactoring ArangoMiGo to be more flexible would be a huge effort, almost a rewrite. Therefore, I created a new project instead.

Why using collection for the name of the graph and view?

The naming is indeed confusing, and I am open to suggestions.

Contributing

Contributions are welcomed! Please open an issue or a pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

View Source
const (
	// DefaultMigrationCollection is the default collection name for the collection storing migrations.
	DefaultMigrationCollection = "migrations"
)

Variables

View Source
var (
	// ErrNoCollection is returned when no collection is provided.
	ErrNoCollection = fmt.Errorf("no collection provided")
	// ErrNoDatabase is returned when no database is provided.
	ErrNoDatabase = fmt.Errorf("no database provided")
)
View Source
var (
	// ErrInvalidMigrationStatus is returned when an invalid migration status is provided.
	ErrInvalidMigrationStatus = fmt.Errorf("invalid migration status")
	// ErrNoMigrations is returned when no migrations are provided.
	ErrNoMigrations = fmt.Errorf("no migrations provided")
	// ErrMigrationFailed is returned when a migration has failed.
	ErrMigrationFailed = fmt.Errorf("migration failed")
)
View Source
var (
	// ErrInvalidOperationKind is returned when an invalid operation kind is
	// specified.
	ErrInvalidOperationKind = fmt.Errorf("invalid operation kind")
)
View Source
var (
	// ErrNoLogger is returned when no logger is provided.
	ErrNoLogger = fmt.Errorf("no logger provided")
)

Functions

This section is empty.

Types

type DefaultLogger

type DefaultLogger struct {
	Writer LogWriter
	Exiter func(int)
}

DefaultLogger is a logger that prints messages to stdout and exits on fatal messages.

func NewDefaultLogger

func NewDefaultLogger() *DefaultLogger

NewDefaultLogger returns a new DefaultLogger.

func (*DefaultLogger) Error

func (l *DefaultLogger) Error(args ...any)

func (*DefaultLogger) Errorf

func (l *DefaultLogger) Errorf(format string, args ...any)

func (*DefaultLogger) Fatal

func (l *DefaultLogger) Fatal(args ...any)

func (*DefaultLogger) Fatalf

func (l *DefaultLogger) Fatalf(format string, args ...any)

func (*DefaultLogger) Info

func (l *DefaultLogger) Info(args ...any)

func (*DefaultLogger) Infof

func (l *DefaultLogger) Infof(format string, args ...any)

type Executor

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

Executor executes migrations on a database in order.

func NewExecutor

func NewExecutor(opts ...ExecutorOption) (*Executor, error)

NewExecutor creates a new Executor. If no migrations are provided, an error is returned.

func (*Executor) Execute

func (e *Executor) Execute(ctx context.Context) error

Execute executes the migrations on the database.

type ExecutorOption

type ExecutorOption func(*Executor) error

ExecutorOption is a function that sets configuration options on Executor.

func WithCollection

func WithCollection(collection string) ExecutorOption

WithCollection sets the collection of the executor.

func WithDatabase

func WithDatabase(db driver.Database) ExecutorOption

WithDatabase sets the database of the executor.

func WithLogger

func WithLogger(logger Logger) ExecutorOption

WithLogger sets the logger of the executor.

func WithMigrations

func WithMigrations(migrations []*Migration) ExecutorOption

WithMigrations sets the migrations of the executor.

type LogWriter

type LogWriter interface {
	WriteString(s string) (n int, err error)
}

LogWriter is an interface that is used to write log messages.

type Logger

type Logger interface {
	// Info logs an informational message.
	Info(args ...any)
	// Infof logs a formatted informational message.
	Infof(format string, args ...any)
	// Error logs an error message.
	Error(args ...any)
	// Errorf logs a formatted error message.
	Errorf(format string, args ...any)
	// Fatal logs a fatal message and exits.
	Fatal(args ...any)
	// Fatalf logs a formatted fatal message and exits.
	Fatalf(format string, args ...any)
}

Logger is an interface that is used to log messages.

type Migration

type Migration struct {
	ID         int             `yaml:"id"`
	Path       string          `yaml:"-"`
	Status     MigrationStatus `yaml:"-"`
	Operations []*Operation    `yaml:"operations"`
}

Migration is a migration that can be run on a database.

func (*Migration) Checksum

func (m *Migration) Checksum() (string, error)

Checksum returns the checksum of the migration. The checksum is the SHA256 hash of the ID and operations of the migration file. It is used to determine if a migration has been run already.

func (*Migration) Migrate

func (m *Migration) Migrate(ctx context.Context, db driver.Database) error

Migrate executes the operations registered to the migration.

func (*Migration) Name

func (m *Migration) Name() string

Name returns the name of the migration. The name is the name of the file without the extension.

type MigrationItem

type MigrationItem struct {
	Key       string          `json:"_key"`
	Name      string          `json:"name"`
	Status    MigrationStatus `json:"status"`
	AppliedAt time.Time       `json:"appliedAt"`
}

MigrationItem represents a migration in the collection.

type MigrationStatus

type MigrationStatus int

MigrationStatus is the status of a migration.

const (
	// MigrationStatusMissing is the status of a migration that is missing.
	MigrationStatusMissing MigrationStatus = iota
	// MigrationStatusRunning is the status of a migration that is currently running.
	MigrationStatusRunning
	// MigrationStatusDone is the status of a migration that has been run.
	MigrationStatusDone
	// MigrationStatusFailed is the status of a migration that has failed.
	MigrationStatusFailed
)

func (*MigrationStatus) MarshalJSON

func (s *MigrationStatus) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (*MigrationStatus) UnmarshalJSON

func (s *MigrationStatus) UnmarshalJSON(status []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

type Operation

type Operation struct {
	Kind       OperationKind  `yaml:"kind"`
	Collection string         `yaml:"collection"`
	Options    map[string]any `yaml:"options"`
}

Operation is an operation to run in a migration.

func (*Operation) GetOperationFn

func (o *Operation) GetOperationFn() (OperationFn, error)

GetOperationFn returns the operation function for the operation kind.

type OperationFn

type OperationFn func(ctx context.Context, db driver.Database) error

OperationFn runs an operation on a database.

func AddEdgeOperation

func AddEdgeOperation(o *Operation) OperationFn

AddEdgeOperation adds an edge definition to a graph.

func AddVertexOperation

func AddVertexOperation(o *Operation) OperationFn

AddVertexOperation adds a vertex collection to a graph.

func CreateCollectionOperation

func CreateCollectionOperation(o *Operation) OperationFn

CreateCollectionOperation creates a collection.

func CreateFulltextIndexOperation

func CreateFulltextIndexOperation(o *Operation) OperationFn

func CreateGeoSpatialIndexOperation

func CreateGeoSpatialIndexOperation(o *Operation) OperationFn

func CreateGraphOperation

func CreateGraphOperation(o *Operation) OperationFn

CreateGraphOperation creates a graph.

func CreateHashIndexOperation

func CreateHashIndexOperation(o *Operation) OperationFn

func CreateInvertedIndexOperation

func CreateInvertedIndexOperation(o *Operation) OperationFn

func CreatePersistentIndexOperation

func CreatePersistentIndexOperation(o *Operation) OperationFn

func CreateSkipListIndexOperation

func CreateSkipListIndexOperation(o *Operation) OperationFn

func CreateTTLIndexOperation

func CreateTTLIndexOperation(o *Operation) OperationFn

func CreateViewOperation

func CreateViewOperation(o *Operation) OperationFn

CreateViewOperation creates a search view.

func CreateZKDIndexOperation

func CreateZKDIndexOperation(o *Operation) OperationFn

func DeleteCollectionOperation

func DeleteCollectionOperation(o *Operation) OperationFn

DeleteCollectionOperation removes a collection.

func DeleteGraphOperation

func DeleteGraphOperation(o *Operation) OperationFn

DeleteGraphOperation deletes a graph.

func DeleteIndexOperation

func DeleteIndexOperation(o *Operation) OperationFn

func DeleteViewOperation

func DeleteViewOperation(o *Operation) OperationFn

DeleteViewOperation removes a search view.

func ExecuteAQLOperation

func ExecuteAQLOperation(o *Operation) OperationFn

ExecuteAQLOperation executes an AQL query.

func RemoveEdgeOperation

func RemoveEdgeOperation(o *Operation) OperationFn

RemoveEdgeOperation removes an edge definition from a graph.

func RemoveVertexOperation

func RemoveVertexOperation(o *Operation) OperationFn

RemoveVertexOperation removes a vertex collection from a graph.

func UpdateCollectionOperation

func UpdateCollectionOperation(o *Operation) OperationFn

UpdateCollectionOperation updates a collection.

func UpdateViewOperation

func UpdateViewOperation(o *Operation) OperationFn

UpdateViewOperation updates an existing search view.

type OperationKind

type OperationKind int

OperationKind is the kind of operation to run.

const (
	OperationKindAQLExecute            OperationKind = iota + 1 // operation to execute an AQL query
	OperationKindCollectionCreate                               // operation to create a collection
	OperationKindCollectionUpdate                               // operation to update a collection
	OperationKindCollectionDelete                               // operation to delete a collection
	OperationKindGraphCreate                                    // operation to create a graph
	OperationKindGraphAddVertex                                 // operation to add a vertex collection to a graph
	OperationKindGraphRemoveVertex                              // operation to remove a vertex collection from a graph
	OperationKindGraphAddEdge                                   // operation to add an edge definition to a graph
	OperationKindGraphRemoveEdge                                // operation to remove an edge definition from a graph
	OperationKindGraphDelete                                    // operation to delete a graph
	OperationKindViewCreate                                     // operation to create a view
	OperationKindViewUpdate                                     // operation to update a view
	OperationKindViewDelete                                     // operation to delete a view
	OperationKindFulltextIndexCreate                            // operation to create a fulltext index
	OperationKindGeoSpatialIndexCreate                          // operation to create a geospatial index
	OperationKindHashIndexCreate                                // operation to create a hash index
	OperationKindInvertedIndexCreate                            // operation to create an inverted index
	OperationKindPersistentIndexCreate                          // operation to create a persistent index
	OperationKindSkipListIndexCreate                            // operation to create a skiplist index
	OperationKindTTLIndexCreate                                 // operation to create a TTL index
	OperationKindZKDIndexCreate                                 // operation to create a ZKD index
	OperationKindIndexDelete                                    // operation to delete an index
)

func (*OperationKind) UnmarshalYAML

func (o *OperationKind) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML implements the yaml.Unmarshaler interface.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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