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 detach support which is compatible with hcl2 used for terraform 0.12 #45

Merged
merged 9 commits into from
Sep 27, 2019

Conversation

btromanova
Copy link
Contributor

@btromanova btromanova commented Aug 15, 2019

This fixes #44

Context:
--detach flag is used in astro to make terraform migrations easier. When terraform resources need to be moved or deleted terraform state subcommands are used to manipulate the state file.
If the remote backend is configured, state file will be uploaded after state manipulation is successful. Which is not desired while migration script is being tested. With detach option astro inits the module (this downloads the state file from remote), modifies the configuration to remove remote backend (which makes terraform use "local" backend) and runs terraform init -force-copy again to re-initialize with new backend. See more in https://www.terraform.io/docs/backends/config.html#unconfiguring-a-backend.

There were two issues with --detach flag that were uncovered by terraform 0.12 upgrade:

  1. astro failed if there was a file in the config that contained terraform block, but didn't contain backend block. This bug exists with the older versions of terraform. This is fixed by changing astDel to astDelIfExists.
  2. To remove remote backend from config, astro walks and modifies the HCL AST tree.
    Terraform 0.12 switched to the new language (HCL2), so old parsing mechanism stopped working.
    I wasn't able to find a way to walk and modify tree with https://godoc.org/github.com/hashicorp/hcl2 (similar issue reported https://github.com/hashicorp/hcl2/issues/23), so I'm using regexp to do this for the simple configurations. That unblocks us for the terraform 0.12 upgrade, but may be not a good solution for the long term.

@btromanova btromanova requested a review from CorgiMan August 15, 2019 13:23
input := []byte(`
terraform {
backend "s3" {}
}

provider "aws" {
region = "us-east-1"
region = "${var.aws_region}"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed this to explicitly show that this is HCL 1.0

Copy link

@CorgiMan CorgiMan left a comment

Choose a reason for hiding this comment

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

LGTM

Take a look at my comment and see if you feel like addressing it :).

// Regexp to find if any backend configuration exists
backendDefinitionRe := regexp.MustCompile(`(?s)[{\s+]backend\s+"[^"]+"\s*{`)
// Regexp to find simple backend configuration, which doesn't contain '{}' inside
backendBlockRe := regexp.MustCompile(`(?s)(\s*backend\s+"[^"]+"\s*{[^{]*?})`)

Choose a reason for hiding this comment

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

would it be easier to, instead of regex, iterate the input as follows:

ix = in.find("\"backend\"")
bracket_count = 0
for ; ix<len(in); ix++ {
  if in[ix]=='{' {
    bracket_count++
  }
  if in[ix]=='}' {
    bracket_count--
    if bracket_count==0 {
      end_ix = ix
      break
    }
  }
}

That way we support complicated configs as well, right? There are some edge cases of course, but this covers a lot more expected cases.

This does assume that the config is correct HCL of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, I thought about this solution. It actually doesn't look as bad at I thought it would.
It's still not ideal, because '{' or '}' can be part of some other lexeme. But in reality this is very unlikely. I'll change the code to what you've suggested, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I implemented suggested approach and got to writing tests, and I'm not sure if it's a good approach anymore.
The problem is that this config

terraform {
         backend "local" {
           constant = "weird_string_{"
         }
       }

Will be recognized as correct expression and turned into

terraform {

I think it's better to fail explicitly in this case.
So once the HCL2 library is ready we should parse config using it, but until then, I'd stick with regexp on simple configs and failures on complex ones.

Copy link
Contributor

@dansimau dansimau left a comment

Choose a reason for hiding this comment

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

Left some comments and questions.

astro/terraform/terraform_remote_state_disable_utils.go Outdated Show resolved Hide resolved
astro/terraform/terraform_remote_state_disable_utils.go Outdated Show resolved Hide resolved
astro/terraform/terraform_remote_state_disable_test.go Outdated Show resolved Hide resolved
astro/terraform/terraform_remote_state_disable_utils.go Outdated Show resolved Hide resolved
// This method should be rewritten once hcl2 supports AST traversal and modification.
func deleteTerraformBackendConfigWithHCL2(in []byte) (updatedConfig []byte, err error) {
// Regexp to find if any backend configuration exists
backendDefinitionRe := regexp.MustCompile(`(?s)[{\s+]backend\s+"[^"]+"\s*{`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do these two regexps need to be different?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made a decision to only work with simple cases, where backend configuration doesn't have other braces inside. However, I want to notify the user, that may have a complex configuration about the error (that backend configuration was not removed).

The first regex is to find whether any backend configuration exists, second to find backend configuration that doesn't have braces inside.

Please let me know, if you think I need to put more comments about it in the code.

// Regexp to find if any backend configuration exists
backendDefinitionRe := regexp.MustCompile(`(?s)[{\s+]backend\s+"[^"]+"\s*{`)
// Regexp to find simple backend configuration, which doesn't contain '{}' inside
backendBlockRe := regexp.MustCompile(`(?s)(\s*backend\s+"[^"]+"\s*{[^{]*?})`)
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ack, thanks for the suggestion, I've added comments.

@btromanova btromanova force-pushed the tr/variable-filters branch 2 times, most recently from 90fa618 to 3144964 Compare September 24, 2019 09:32
@btromanova btromanova changed the base branch from tr/variable-filters to master September 24, 2019 09:51
expected string
}{
{
config: `
Copy link
Contributor

Choose a reason for hiding this comment

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

This still looks weird. Is it because of a tabs/spaces mix?

Copy link
Contributor

@dansimau dansimau left a comment

Choose a reason for hiding this comment

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

Fix the tab/space issue but after that LGTM! THANK YOU FOR YOUR FANTASTIC WORK!!!

@btromanova btromanova merged commit f15067e into master Sep 27, 2019
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.

Detach flag is broken after update to 0.12
3 participants