Skip to content

Commit

Permalink
atlas/migration: protected_flows configurations for migrate down
Browse files Browse the repository at this point in the history
  • Loading branch information
giautm committed Aug 8, 2024
1 parent 285e875 commit e0d623c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 7 deletions.
18 changes: 18 additions & 0 deletions docs/resources/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ resource "atlas_migration" "hello" {
- `dir` (String) the URL of the migration directory. dir or remote_dir block is required
- `env_name` (String) The name of the environment used for reporting runs to Atlas Cloud. Default: tf
- `exec_order` (String) How Atlas computes and executes pending migration files to the database. One of `linear`,`linear-skip` or `non-linear`. See https://atlasgo.io/versioned/apply#execution-order
- `protected_flows` (Block, Optional) ProtectedFlows defines the protected flows of a deployment. (see [below for nested schema](#nestedblock--protected_flows))
- `remote_dir` (Block, Optional, Deprecated) (see [below for nested schema](#nestedblock--remote_dir))
- `revisions_schema` (String) The name of the schema the revisions table resides in
- `timeouts` (Block, Optional) (see [below for nested schema](#nestedblock--timeouts))
Expand All @@ -59,6 +60,23 @@ Optional:
- `url` (String)


<a id="nestedblock--protected_flows"></a>
### Nested Schema for `protected_flows`

Optional:

- `migrate_down` (Block, Optional) DeploymentFlow defines the flow of a deployment. (see [below for nested schema](#nestedblock--protected_flows--migrate_down))

<a id="nestedblock--protected_flows--migrate_down"></a>
### Nested Schema for `protected_flows.migrate_down`

Optional:

- `allow` (Boolean) Allow allows the flow to be executed.
- `auto_approve` (Boolean) AutoApprove allows the flow to be automatically approved.



<a id="nestedblock--remote_dir"></a>
### Nested Schema for `remote_dir`

Expand Down
68 changes: 66 additions & 2 deletions internal/provider/atlas_migration_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ type (
MigrationResource struct {
providerData
}
DeploymentFlow struct {
Allow types.Bool `tfsdk:"allow"`
AutoApprove types.Bool `tfsdk:"auto_approve"`
}
// MigrationResourceModel describes the resource data model.
MigrationResourceModel struct {
Config types.String `tfsdk:"config"`
Expand All @@ -49,8 +53,11 @@ type (
Baseline types.String `tfsdk:"baseline"`
ExecOrder types.String `tfsdk:"exec_order"`

Cloud *AtlasCloudBlock `tfsdk:"cloud"`
RemoteDir *RemoteDirBlock `tfsdk:"remote_dir"`
Cloud *AtlasCloudBlock `tfsdk:"cloud"`
RemoteDir *RemoteDirBlock `tfsdk:"remote_dir"`
ProtectedFlows *struct {
MigrateDown *DeploymentFlow `tfsdk:"migrate_down"`
} `tfsdk:"protected_flows"`

EnvName types.String `tfsdk:"env_name"`
Status types.Object `tfsdk:"status"`
Expand Down Expand Up @@ -81,6 +88,19 @@ var (
"next": types.StringType,
"latest": types.StringType,
}
deploymentFlow = schema.SingleNestedBlock{
Description: "DeploymentFlow defines the flow of a deployment.",
Attributes: map[string]schema.Attribute{
"allow": schema.BoolAttribute{
Description: "Allow allows the flow to be executed.",
Optional: true,
},
"auto_approve": schema.BoolAttribute{
Description: "AutoApprove allows the flow to be automatically approved.",
Optional: true,
},
},
}
)

// NewMigrationResource returns a new MigrateResource.
Expand All @@ -105,6 +125,12 @@ func (r *MigrationResource) Schema(ctx context.Context, _ resource.SchemaRequest
Blocks: map[string]schema.Block{
"cloud": cloudBlock,
"remote_dir": remoteDirBlock,
"protected_flows": schema.SingleNestedBlock{
Description: "ProtectedFlows defines the protected flows of a deployment.",
Blocks: map[string]schema.Block{
"migrate_down": deploymentFlow,
},
},
"timeouts": timeouts.Block(ctx, timeouts.Opts{
Create: true,
Update: true,
Expand Down Expand Up @@ -288,13 +314,33 @@ func (r MigrationResource) ValidateConfig(ctx context.Context, req resource.Vali
"cloud is unset", "cloud is required when using atlas:// URL",
)
}
if f := data.ProtectedFlows; f != nil {
if d := f.MigrateDown; d != nil {
if d.Allow.ValueBool() && d.AutoApprove.ValueBool() {
resp.Diagnostics.AddError(
"protected flow error", "autoApprove is not allowed for a remote directory",
)
return
}
}
}
return
default:
// Local dir, validate config for dev-url
resp.Diagnostics.Append(r.validateConfig(ctx, req.Config)...)
if resp.Diagnostics.HasError() {
return
}
if f := data.ProtectedFlows; f != nil {
if d := f.MigrateDown; d != nil {
if d.Allow.ValueBool() && !d.AutoApprove.ValueBool() {
resp.Diagnostics.AddError(
"protected flow error", "allow cannot be true without auto_approve for local migration directory",
)
return
}
}
}
}
// Validate the remote_dir block
switch {
Expand Down Expand Up @@ -481,6 +527,10 @@ func (r *MigrationResource) migrate(ctx context.Context, data *MigrationResource
}
switch {
case len(status.Pending) == 0 && len(status.Applied) > 0 && len(status.Applied) > len(status.Available):
if !cfg.MigrateDown {
diags.AddError("Protected flow error", "migrate down is not allowed, set `migrate_down.allow` to true to allow downgrade")
return
}
params := &atlas.MigrateDownParams{
Env: cfg.EnvName,
Vars: cfg.Vars,
Expand Down Expand Up @@ -787,6 +837,20 @@ func (d *MigrationResourceModel) projectConfig(cloud *AtlasCloudBlock, devURL st
if err != nil {
return nil, err
}
if f := d.ProtectedFlows; f != nil {
if d := f.MigrateDown; d != nil && d.Allow.ValueBool() {
if strings.HasPrefix(cfg.Env.Migration.DirURL, "atlas://") {
if d.AutoApprove.ValueBool() {
return nil, fmt.Errorf("autoApprove is not allowed for a remote directory")
}
} else {
if !d.AutoApprove.ValueBool() {
return nil, fmt.Errorf("allow cannot be true without autoApprove for local migration directory")
}
}
cfg.MigrateDown = true
}
}
if vars := d.Vars.ValueString(); vars != "" {
if err = json.Unmarshal([]byte(vars), &cfg.Vars); err != nil {
return nil, fmt.Errorf("failed to parse variables: %w", err)
Expand Down
20 changes: 20 additions & 0 deletions internal/provider/atlas_migration_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,11 @@ func TestAccMigrationResource_AtlasURL_WithTag(t *testing.T) {
resource "atlas_migration" "hello" {
url = "%[3]s"
dir = "atlas://test?tag=one-down"
protected_flows {
migrate_down {
allow = true
}
}
}
`, devURL, srv.URL, dbURL)
resource.Test(t, resource.TestCase{
Expand Down Expand Up @@ -801,6 +806,11 @@ func TestAccMigrationResource_RequireApproval(t *testing.T) {
resource "atlas_migration" "hello" {
url = "%[3]s"
dir = "atlas://test?tag=latest"
protected_flows {
migrate_down {
allow = true
}
}
}`, devURL, srv.URL, dbURL),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("atlas_migration.hello", "id", "remote_dir://test"),
Expand Down Expand Up @@ -831,6 +841,11 @@ func TestAccMigrationResource_RequireApproval(t *testing.T) {
resource "atlas_migration" "hello" {
url = "%[3]s"
dir = "atlas://test?tag=tag3"
protected_flows {
migrate_down {
allow = true
}
}
}`, devURL, srv.URL, dbURL),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr("atlas_migration.hello", "id", "remote_dir://test"),
Expand Down Expand Up @@ -860,6 +875,11 @@ func TestAccMigrationResource_RequireApproval(t *testing.T) {
resource "atlas_migration" "hello" {
url = "%[3]s"
dir = "atlas://test?tag=tag2"
protected_flows {
migrate_down {
allow = true
}
}
}`, devURL, srv.URL, dbURL),
ExpectError: regexp.MustCompile("migration plan was aborted"),
},
Expand Down
12 changes: 7 additions & 5 deletions internal/provider/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import (
type (
// projectConfig is the builder for the atlas.hcl file.
projectConfig struct {
EnvName string
Cloud *cloudConfig
Env *envConfig
Config string
Vars atlas.Vars2
Cloud *cloudConfig
Env *envConfig

Config string // The base atlas.hcl to merge with, provided by the user
Vars atlas.Vars2 // Variable supplied for atlas.hcl
EnvName string // The env name to report
MigrateDown bool // Allow TF run migrate down when detected
}
envConfig struct {
URL string
Expand Down

0 comments on commit e0d623c

Please sign in to comment.