[go: nahoru, domu]

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 terraform 0.12 #57

Closed
wants to merge 13 commits into from
Closed

Conversation

sbalbach
Copy link
Contributor
@sbalbach sbalbach commented Jul 8, 2019

This may not be perfect but works for my needs in getting terrascan to run with terraform 0.12. It is still backward compatible with previous terraform version.

See issue #55.

@pop
Copy link
pop commented Jul 11, 2019

@sbalbach https://travis-ci.org/virtuald/pyhcl/jobs/555816019#L209 Looks like black doesn't like your code formatting. Mind running black on your PR and making the appropriate fixes?

(I have a lot of interest in this PR getting merged, hence the unsolicited drive-by review).

@virtuald
Copy link
Owner

Sorry for the delayed response, I've been traveling.

Can you add some new simple tests for the new syntax that you're supporting? Otherwise someone is going to break it in the future.

@sbalbach
Copy link
Contributor Author
sbalbach commented Jul 17, 2019

I seem to have found an issue with EPLUS. I have reproduced it in the test_lexer.py by adding the following line to the COMPLEX_TOKEN_FIXTURES.

(["IDENTIFIER", "EQUAL", "LEFTBRACKET", "IDENTIFIER", "LEFTBRACKET", "NUMBER", "RIGHTBRACKET", "PERIOD", "EPLUS", "IDENTIFIER", "RIGHTBRACKET", ], "records = [aws_elasticsearch_domain.elasticsearch[0].endpoint]"),

That will pass the test even though there is no actual scientific notation in the string. The problem is that the '+' is optional. Since there is a period followed by an e, an EPLUS gets thrown into the mix which is causing me grief.

EPLUS is defined as:

r'(?<=\d|\.)[eE]\+?'

If I remove the ?, the EPLUS doesn't get added to the above test case but that will fail other tests because valid scientific notation is 0.e

@sbalbach
Copy link
Contributor Author

I believe I've found a solution for the EPLUS issue:

r'(?<=\d)[eE]\+?|(?<=\d\.)[eE]\+?'

This passes all of the tests and doesn't give me an erroneous EPLUS from

records = [aws_elasticsearch_domain.elasticsearch[0].endpoint]

I'll work on new test cases next.

@PetrusHahol
Copy link
PetrusHahol commented Jul 31, 2019

Good evening. I have tried to use this solution and faced a problem to parse .tf file with Conditional Operator

resource "openstack_compute_instance_v2" "instance_name" {
  availability_zone = var.cloud_release == "li" ? var.ghost-web_az : element(var.availability_zones, count.index)
}

Error is:

self = <hcl.parser.HclParser object at 0x10584ad68>, p = LexToken(EQ,'==',6,266)
E       ValueError: Line 6, column 266: unexpected EQ

Is something wrong with my syntax?

…bjectkey EQUAL objectkey QMARK objectkey COLON function). Also add tests for these situations.
@sbalbach
Copy link
Contributor Author

@PetrusHahol : So many permutations...I just uploaded a fix. Give it a try.

@PetrusHahol
Copy link

@PetrusHahol : So many permutations...I just uploaded a fix. Give it a try.

I've tested, now it works, thank you!

@PetrusHahol
Copy link
PetrusHahol commented Aug 1, 2019

Thank you once again,
but I faced one more problem with converting from HCL to python object.

variable "list_variable" {
  type    = list(string)
  default = ["value1", "value2", "value3"]
}

After converting type looks like:

     "type": [   "list", "string"   ]

And terraform plan fails with error:

A type specification is either a primitive type keyword (bool, number, string)
or a complex type constructor call, like list(string).

According to documentation of tf0.12 it should be "type": "list(string)"

Also
template = file("templates/cluster.tpl")
name = format("instance%02d", count.index + 1)
web = join("\n", data.template_file.cluster.*.rendered)

@sbalbach
Copy link
Contributor Author
sbalbach commented Aug 5, 2019

According to documentation of tf0.12 it should be "type": "list(string)"

Can you post a link to the documentation that says this?

@PetrusHahol
Copy link

According to documentation of tf0.12 it should be "type": "list(string)"

Can you post a link to the documentation that says this?

Sure, https://www.terraform.io/docs/configuration/types.html

@PetrusHahol
Copy link
PetrusHahol commented Aug 5, 2019

Also I faced lots of additional issues with hcl parser. Should I create issues in your repository? Or I can describe all of them here.
I fixed few of them in my forked repo.

@sbalbach
Copy link
Contributor Author
sbalbach commented Aug 5, 2019

According to documentation of tf0.12 it should be "type": "list(string)"

Can you post a link to the documentation that says this?

Sure, https://www.terraform.io/docs/configuration/types.html

Well, the terraform doc doesn't actually say how to convert from terraform to a python object because they don't deal with python objects.

I guess I'm confused on how you are using pyhcl. You're converting your terraform files to python objects and then running terraform on them somehow? Why aren't you just running terraform on your terraform files? I'm apparently missing something...

@PetrusHahol
Copy link
PetrusHahol commented Aug 5, 2019

According to documentation of tf0.12 it should be "type": "list(string)"

Can you post a link to the documentation that says this?

Sure, https://www.terraform.io/docs/configuration/types.html

Well, the terraform doc doesn't actually say how to convert from terraform to a python object because they don't deal with python objects.

I guess I'm confused on how you are using pyhcl. You're converting your terraform files to python objects and then running terraform on them somehow? Why aren't you just running terraform on your terraform files? I'm apparently missing something...

So, in my solution(of my team) all terraform files in 11 version can be specified as .json and .tf as well and all of them written by users.

After terraform init we have internal logic which changes a lot of data in .tf files.

Unfortunately we can't write back .tf files after changes in hcl format because there is no libraries which allow to do it in python. Because of that we need to convert everything to .json

Now we are going to upgrade our solution to Terraform version 12 and still need to convert all .tf files to json before apply

@sbalbach
Copy link
Contributor Author
sbalbach commented Aug 5, 2019

Ok, I understand now. I've put a new version out there. Give it a try.

returnValue = ""
if type(value) is dict:
returnValue += "{"
isFirstTime = True
Copy link
Owner
@virtuald virtuald Aug 5, 2019

Choose a reason for hiding this comment

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

For each of these cases, it feels more pythonic to do something like

returnValue = "{" + ",".join(key + ":" + self.flatten(value[key]) for key in value) + "}"

@virtuald
Copy link
Owner
virtuald commented Aug 5, 2019

Is there an issue with ternary operations with spaces? Other than that and my comment above, this looks pretty good and I'd be happy to merge after those are addressed.

@PetrusHahol
Copy link
PetrusHahol commented Aug 6, 2019

Some problems surfaced and few tests of ours are failed.
name = format("name_%02d", count.index + 1)
has been converted to:
"name": "format(name_%02d,count.index,+,1)"

There are two additional commas and no space between IDENTIFIER's. One of the commits how I added spaces

Also for all values without double quotes should be added interpolation
Tests are here

@PetrusHahol
Copy link

So, I have fixed all of issues. We have almost 5000 lines of hcl which converted with logic in my fork successfully. Once I will finally finish with testing, I will create pull request to @sbalbach fork.

@virtuald It would be great to create release or tag before this pull will be merged because there is no backward compatibility.

@sbalbach
Copy link
Contributor Author
sbalbach commented Aug 7, 2019

I'm going to be on vacation for the next week+ so I've put out the changes that I had which should address what has already been pointed out.

@virtuald
Copy link
Owner
virtuald commented Aug 8, 2019

@PetrusHahol this particular PR doesn't seem to break backwards compatibility as far as I can see (as all of the old test fixtures seem to work?).

@sbalbach
Copy link
Contributor Author
sbalbach commented Aug 8, 2019

I haven't seen any backward compatibility issues either. I'd suggest a major version change, though.

@PetrusHahol
Copy link
PetrusHahol commented Aug 8, 2019

So, terraform 11 syntax contained interpolation everywhere in double quotes. Before(in tf11) every time we injected another variable via "${expression}"
With terraform 12 they deleted double quotes and interpolation as well, but it doesn't work with json. In json syntax we need to add interpolation back. Few examples :

  • Example1:
    Before:
    hcl resource_name_output = ["${resource1.name1}", "${resource2.name2}"]
    json { "resource_name_output" : ["${resource1.name1}", "${resource2.name2}"] }
    Now:
    hcl resource_name_output = [resource1.name1, resource2.name2]
    json { "resource_name_output" : ["${resource1.name1}", "${resource2.name2}"] } <= interpolation has to appear.

  • Example2:
    Before:
    hcl bar = "${file("bing/bong.txt")}"
    json { "bar": "${file("bing/bong.txt")}" }
    Now:
    hcl bar = file("bing/bong.txt")
    json { "bar": "${file(\"bing/bong.txt\")" } <= interpolation has to appear.

  • Example3:
    Before:
    hcl cluster_3 = "${format("name_%02d", count.index + 1)}"
    json { "cluster_3 ": "${format(\"name_%02d\", count.index + 1)}"
    Now:
    hcl cluster_3 = format("name_%02d", count.index + 1)
    json { "cluster_3 ": "${format(\"name_%02d\", count.index + 1)}" <= interpolation has to appear.

  • Example4:
    Before:
    hcl value = "string"
    json { "value ": "string"}
    Now:
    hcl value = "string"
    json { "value ": "string" } <= no interpolation (just a string)

Two exceptions :

  1. type of variable list(string), map(string), etc should be without interpolation
  2. depends_on values should be without interpolation

If interpolation will not be added, terraform will work with values as strings.
https://github.com/PetrusHahol/pyhcl I changed a lot of tests, added couple of new and added additional token - STRING_IDENTIFIER with r'"(?:[^\\"]|\.)*"' regular expression. We need to separate string with double quotes and without.

@PetrusHahol
Copy link

Pull request to @sbalbach fork is here .

"object": {
"vars": {
"cluster_1": "join(\\n,data.template_file.cluster_1.*.rendered)",
"cluster_2": "join(\\n,data.template_file.cluster_2.*.rendered)",
Copy link
Owner

Choose a reason for hiding this comment

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

These are weird. Where did the quotes around \n and name_%02d go?

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 just noticed this comment was probably directed at me. :)
It's not clear to me how those should look in json. Should there be quotes there? I assume they would have to be escaped if there were there, right?

Copy link
Owner

Choose a reason for hiding this comment

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

Yes, the quotes should be there, and they should be escaped.

"cluster_1": "join(\"\\n\", data.template_file.cluster_1.*.rendered)"

@virtuald
Copy link
Owner
virtuald commented Aug 9, 2019

@PetrusHahol I'm a bit confused by your examples, but I suppose that I don't really track HCL anymore. Keep in mind this is not a terraform library, it is an HCL library (for example, other hashicorp products such as nomad use HCL). Things like interpolation/etc are specific to terraform, and would belong in a pyterraform library.

@PetrusHahol
Copy link
PetrusHahol commented Aug 9, 2019

@PetrusHahol I'm a bit confused by your examples, but I suppose that I don't really track HCL anymore. Keep in mind this is not a terraform library, it is an HCL library (for example, other hashicorp products such as nomad use HCL). Things like interpolation/etc are specific to terraform, and would belong in a pyterraform library.

Just realised that changes I did it's for hcl2

From my point of view, It means this pull requests should not be here. Terraform 12 uses fully another hcl parser (hcl2).

@sbalbach
Copy link
Contributor Author

@PetrusHahol this is for the changes brought about by terraform 0.12. As I said in issue #57 Hashicorp plans at some future date to combine HCL & HCL2. The changes that I had made were all backward compatible with HCL. I don't use the conversion to json option, though; I use the python objects in memory that pyhcl creates. I haven't had a chance since I got back from vacation to try your changes with my other packages to see if or how badly they get broken. Hopefully, I will have a chance to do that Monday.

@sbalbach
Copy link
Contributor Author

@PetrusHahol I've run into a problem with your STRING_IDENTIFER on the below line:

value = "urn:amazon:cognito:sp:${join("", aws_cognito_user_pool.elasticsearch.*.id)}"

It matches urn:amazon:cognito:sp:${join( as a STRING_IDENTIFIER and another one as , aws_cognito_user_pool.elasticsearch..id)} when there should be only one that is the entire urn:amazon:cognito:sp:${join("", aws_cognito_user_pool.elasticsearch..id)} because the inner "" is inside of the ${}. I'm not good enough at regex to figure out how to define that. :(

@sbalbach
Copy link
Contributor Author

@PetrusHahol it seems that you are basically transforming from terraform 0.12 to the old terraform 0.11 syntax. I don't believe that is the correct way to handle it.
Also, if you are transforming terraform to json, be aware of this issue.

@PetrusHahol
Copy link

@sbalbach Sorry that didn't answer long time. So, basically you are right about converting to old syntax, but it seems like this is right way. I will check your example which does not work, but it seems you just need to add \ before " and it will look like

value = "urn:amazon:cognito:sp:${join(\"\", aws_cognito_user_pool.elasticsearch.*.id)}"

@sbalbach
Copy link
Contributor Author

@sbalbach Sorry that didn't answer long time. So, basically you are right about converting to old syntax, but it seems like this is right way. I will check your example which does not work, but it seems you just need to add \ before " and it will look like

value = "urn:amazon:cognito:sp:${join(\"\", aws_cognito_user_pool.elasticsearch.*.id)}"

The hcl is valid as it is. I can't force the users to conform to this tool if they are writing valid hcl.

@marco-m
Copy link
marco-m commented Sep 25, 2019

Hello, what is the status of this PR? Thanks :-)

Copy link
Owner
@virtuald virtuald left a comment

Choose a reason for hiding this comment

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

If the escaped quotes can be fixed, I'm fine to merge this.

@sbalbach
Copy link
Contributor Author
sbalbach commented Oct 2, 2019

I have given up trying to make this work for everyone else's needs. I have a version that works for what I need but breaks with no fix in sight if I make changes to address some of the later issues that were brought up.

@aoskotsky-amplify
Copy link

I've made another package just for hcl2 that could help https://pypi.org/project/python-hcl2/. Probably doesn't cover every use case yet though

@virtuald
Copy link
Owner
virtuald commented Jan 8, 2020

Fixed in #63

@virtuald virtuald closed this Jan 8, 2020
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.

None yet

6 participants