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

hclwrite support for a list of traversals #347

Open
rifelpet opened this issue Feb 23, 2020 · 4 comments
Open

hclwrite support for a list of traversals #347

rifelpet opened this issue Feb 23, 2020 · 4 comments

Comments

@rifelpet
Copy link

Hi, I'm working on writing some terraform configuration using hclwrite and I'm wondering how to best define an attribute's value as a list of traversals. I see examples for lists via cty.Value and SetAttributeValue but nothing for traversals. Is it possible with SetAttributeTraversal or do I need to resort to SetAttributeRaw?

Heres a simplified version of what I have:

package main

import (
	"fmt"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclwrite"
)

func main() {
	f := hclwrite.NewEmptyFile()
	rootBody := f.Body()
	resBlock := rootBody.AppendNewBlock("resource", []string{"aws_route53_record", "foo"})
	resBody := resBlock.Body()
	traversal := hcl.Traversal{
		hcl.TraverseRoot{Name: "local"},
		hcl.TraverseAttr{Name: "name"},
	}
	resBody.SetAttributeTraversal("name", traversal)
	fmt.Printf("%s\n", f.Bytes())
}

HCL Template

resource "aws_route53_record" "foo" {
  name = local.name
}

What I'm trying to achieve is something like:

resource "aws_route53_record" "foo" {
  name = local.name

  records = [local.foo, local.bar]
}

I don't see any examples in the docs or any test cases that do this, so any example code for this would be appreciated. thanks!

@rifelpet
Copy link
Author

rifelpet commented Mar 2, 2020

@apparentlymart can you suggest how to best accomplish this?

@apparentlymart
Copy link
Contributor

apparentlymart commented Mar 5, 2020

Hi @rifelpet,

Unfortunately right now the hclwrite functionality is focused mainly on the simple sorts of cases that tend to arise when we're serializing static data into HCL using the gohcl package, and so support for more general expression construction is lacking, as you've seen. I think you're right that with the current release SetAttributeRaw is the only way to get this done.

The intent here was to eventually expand the API for hclwrite.Expression to have more constructor functions to construct different sorts of expressions (it currently only supports traversals and literals) and then have a method on body like SetAttributeExpr which takes an arbitrary hclwrite.Expression. At that point, Body.SetAttributeTraversal and Body.SetAttributeValue would be convenience wrappers around constructing those common sorts of expressions and setting them all in one step. Then your example might look something like this (using some hypothetical method names I just made up on the spot here):

package main

import (
	"fmt"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclwrite"
)

func main() {
	f := hclwrite.NewEmptyFile()
	rootBody := f.Body()
	resBlock := rootBody.AppendNewBlock("resource", []string{"aws_route53_record", "foo"})
	resBody := resBlock.Body()
	traversal := hcl.Traversal{
		hcl.TraverseRoot{Name: "local"},
		hcl.TraverseAttr{Name: "name"},
	}
	resBody.SetAttributeTraversal("name", traversal)

	recordsExpr := hclwrite.NewExpressionTuple(
                hclwrite.NewExpressionAbsTraversal(hcl.Traversal{
			hcl.TraverseRoot{Name: "local"},
			hcl.TraverseAttr{Name: "foo"},
		}),
                hclwrite.NewExpressionAbsTraversal(hcl.Traversal{
			hcl.TraverseRoot{Name: "local"},
			hcl.TraverseAttr{Name: "bar"},
		}),
        )
        resBody.SetAttributeExpr(recordsExpr)

	fmt.Printf("%s\n", f.Bytes())
}

If memory serves (I'm afraid it's been a while) the tricky thing here was in robustly managing the attaching and detaching of AST nodes. We started with Body.SetAttributeTraversal and Body.SetAttributeValue because that API allowed us to avoid dealing with that problem at first by keeping the intermediate expression hidden inside the function.

In the long run, I'd hoped to implement a model somewhat like an HTML/XML DOM where it's possible for leaf nodes to start their lives detached and then be attached into a document by a subsequent call. That requires additional state internally so that the nodes can know what they are attached to (and whether they are attached to anything at all), which is there in part but I think not 100% complete in the current implementation.

@floresj
Copy link
Contributor

floresj commented Mar 5, 2020

@apparentlymart I truly appreciate the time and effort you put into your responses to these issues. They're always in-depth and extremely informative. I've learned a lot by just reading your responses. Thanks a bunch.

@rifelpet
Copy link
Author

rifelpet commented Mar 7, 2020

Thank you for the detailed response! I'll go ahead and use SetAttributeRaw for now and keep an eye on updates to this library.

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

No branches or pull requests

3 participants