Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for migration history management #2

Merged
merged 38 commits into from
Nov 14, 2020
Merged

Add support for migration history management #2

merged 38 commits into from
Nov 14, 2020

Conversation

minamijoyo
Copy link
Owner

@minamijoyo minamijoyo commented Sep 17, 2020

In the analogy of database migration, I believe a migration history management feature will be definitely useful.
This PR allow us to set a migration file directory and history storage in the configuration file, and apply all pending unapplied migrations in sequence.

Migration files will be applied in alphabetical order by filename. You can use a serial number for filename (e.g. 123.hcl), but I recommend you to use a timestamp as a prefix to avoid git conflicts (e.g. 20201114000000_mv_bar1.hcl)

A history file serializes and saves the History object in JSON format. It also contains format version in case of format change. An example of history file as follows:

{
    "version": 1,
    "records": {
        "20201114000000_mv_bar1.hcl": {
            "type": "state",
            "name": "bar1",
            "applied_at": "2020-11-14T23:18:38.743079+09:00"
        },
        "20201114000001_mv_bar2.hcl": {
            "type": "state",
            "name": "bar2",
            "applied_at": "2020-11-14T23:21:18.976782+09:00"
        }
    }
}

The history file is stored in a Storage. Since Terraform supports multiple cloud storages to store tfstate, so we probably need to support them to store the migration history, but the Terraform's backend implementation cannot be reused as it is, so I implemented it by myself only we need with a primitive SDK. There is also an external library that abstracts cloud storage, but I didn't use it because I will probably want full control authentication options to match the behavior with terraform.
I had another option which ​​implementing a custom terraform provider for tfmigrate, but I thought it wouldn't match the provider's resource model because migration isn't idempotent and its order is important.

This PR implemented local and s3 storage. The Storage interface is simple, so it's easy to add implementations from other cloud providers. If your cloud provider is not supported for now, as a workaround, you can use local storage and synchronize it to your cloud storage with a wrapper script.

A new configuration file has been added to specify the migration directory and history storage. The configuration file path defaults to .tfmigrate.hcl. You can change the path of configuration file with command line flag --config. An example of configuration file as follows:

tfmigrate {
  migration_dir = "./tfmigrate"
  history {
    storage "local" {
      path = ".tfmigrate_history.json"
    }
  }
}
tfmigrate {
  migration_dir = "./tfmigrate"
  history {
    storage "s3" {
      bucket = "example-tfmigrate-test"
      key    = "tfmigrate/history.json"
    }
  }
}

The plan / apply command applies all unapplied migrations if the argument is omitted, but you can plan / apply only a given migration file by the argument.

$ tfmigrate apply --help
Usage: tfmigrate apply [PATH]

Apply computes a new state and pushes it to remote state.
It will fail if terraform plan detects any diffs with the new state.

Arguments
  PATH               A path of migration file
                     Required in non-history mode. Optional in history-mode.

Options:
  --config           A path to tfmigrate config file

If the migration file does not exist, a type of error depends on the
cloud storage, so it is useful not to return an error if the key is
not initialized.
To avoid a circular reference of config and history package, we should
move config.HistoryConfig to history package. Before do it, we also move
config.Migrator to tfmigrate package as well.
We use a map to check if a given migration has been applied easily.
In addition, we will probably need more meta data of migration
to provider a better UX.
Copy link

@hermanschaaf hermanschaaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this would be awesome! What is the pull request still missing?

config/storage.go Outdated Show resolved Hide resolved
@minamijoyo
Copy link
Owner Author

@hermanschaaf Hi, thank you for your interest!

A persistence mechanism of history has been almost completed and connecting it to migrator implementations and UIs should be done before merge.

As I'm working on multiple side projects in my spare time, so the progress is a bit slow, but I believe it's a definitely helpful feature for us. Stay tuned!

@minamijoyo minamijoyo changed the title [WIP] Add support for migration history management Add support for migration history management Nov 14, 2020
@minamijoyo
Copy link
Owner Author

minamijoyo commented Nov 14, 2020

There are still some things I would like to fix, such as s3 authentication options, but the diff is too big and the minimum use-case works for now, so I'll merge it and fix them in the following PRs.

@minamijoyo minamijoyo merged commit 8cb12fb into master Nov 14, 2020
@minamijoyo minamijoyo deleted the history branch November 14, 2020 16:21
minamijoyo added a commit that referenced this pull request Nov 16, 2020
In #2, I added s3 storage with a primitive aws-sdk-go, but the initial
implementation doesn't work with a localstack environment because it
requires force_path_style option. In addition, I want to use a explicit
profile setting for multi-accounts environment rather than implicit
access key via environment variable. Since Terraform's s3 backend has
many authentication options, I can imagine some people want to other
options and they expect the order of reading credentials to be the
same as Terraform.

Fortunately, The Terraform s3 backend and AWS provider authentication
logic is split into an external library named hashicorp/aws-sdk-go-base.
I think using the same library as Terraform will reduce confusion.

However, there are many minor options and it's a pain to test all
options from first, so I added only options I need for now. If something
missing, feel free to open an issue or submit a pull request.
@minamijoyo
Copy link
Owner Author

@hermanschaaf Released in v0.2.0 🚀

minamijoyo pushed a commit that referenced this pull request Mar 15, 2022
minamijoyo added a commit that referenced this pull request Aug 15, 2023
Update golangci-lint to v1.45.2 and actions to latest
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants