1

am looking for a way to find the type of variable using go-cty package in hclwrite.

My aim is to generate a variables file like below

variable "test_var" {
  val1 = bool
  val2 = string
  val3 = number
}

reference: https://developer.hashicorp.com/terraform/language/values/variables

I am using the below code to generate this.

    vars := hclwrite.NewEmptyFile()
    vars_root_body := vars.Body()
    vars_file, vars_create_err := os.Create("variables.tf")
    logErrors(vars_create_err)
    vars_block := vars_root_body.AppendNewBlock("variable",[]string{"test_var"})
    vars_block_body := vars_block.Body()

    vars_block_body.SetAttributeValue("val", cty.Value{})

    _, vars_write_err := vars_file.Write(vars.Bytes())
    logErrors(vars_write_err)
    defer vars_file.Close()

the above code generates this

variable "test_var" {
  val = null
}

I want to fetch the type of that variable and set the attribute value based on that type, as show in the reference link above. I tried lot of ways but didn't get anything. Can someone please help me on this?

I tried the above code and lot of other ways like

cty.SetValEmpty(cty.Bool)

but it didn't work.

blackgreen
  • 34,072
  • 23
  • 111
  • 129
  • Mart helps maintain that package (`go-cty`) and visits here occasionally, so I will defer to him to write a better answer than I would attempt. – Matthew Schuchard Nov 07 '22 at 18:11

1 Answers1

3

The expected syntax for a variable block in Terraform includes an argument named type, not an argument named val. From your example I assume that you are intending to populate type.

The type constraint syntax that Terraform uses is not directly part of HCL and so there isn't any built-in way to generate that syntax in only one step. However, type constraint are built from HCL's identifier and function call syntaxes, and hclwrite does have some functions for helping to generate those as individual parts:

    f := hclwrite.NewEmptyFile()
    rootBody := f.Body()
    varBlock := rootBody.AppendNewBlock("variable", []string{"example"})
    varBody := varBlock.Body()
    varBody.SetAttributeRaw(
        "type",
        hclwrite.TokensForFunctionCall(
            "set",
            hclwrite.TokensForIdentifier("string"),
        ),
    )
    fmt.Printf("%s", f.Bytes())

The above will generate the following:

variable "example" {
  type = set(string)
}

If you already have a cty.Value value then you can obtain its type using the Type method. However, as mentioned above there isn't any ready-to-use function for converting a type into a type expression, so if you want to be able to generate a type constraint for any value then you'd need to write a function for this yourself, wrapping the TokensForFunctionCall and TokensForIdentifier functions. For example:

package main

import (
    "fmt"
    "sort"

    "github.com/hashicorp/hcl/v2/hclwrite"
    "github.com/zclconf/go-cty/cty"
)

func main() {
    f := hclwrite.NewEmptyFile()
    rootBody := f.Body()
    varBlock := rootBody.AppendNewBlock("variable", []string{"example"})
    varBody := varBlock.Body()
    varBody.SetAttributeRaw(
        "type",
        typeExprTokens(cty.Set(cty.String)),
    )
    fmt.Printf("%s", f.Bytes())
}

func typeExprTokens(ty cty.Type) hclwrite.Tokens {
    switch ty {
    case cty.String:
        return hclwrite.TokensForIdentifier("string")
    case cty.Bool:
        return hclwrite.TokensForIdentifier("bool")
    case cty.Number:
        return hclwrite.TokensForIdentifier("number")
    case cty.DynamicPseudoType:
        return hclwrite.TokensForIdentifier("any")
    }

    if ty.IsCollectionType() {
        etyTokens := typeExprTokens(ty.ElementType())
        switch {
        case ty.IsListType():
            return hclwrite.TokensForFunctionCall("list", etyTokens)
        case ty.IsSetType():
            return hclwrite.TokensForFunctionCall("set", etyTokens)
        case ty.IsMapType():
            return hclwrite.TokensForFunctionCall("map", etyTokens)
        default:
            // Should never happen because the above is exhaustive
            panic("unsupported collection type")
        }
    }

    if ty.IsObjectType() {
        atys := ty.AttributeTypes()
        names := make([]string, 0, len(atys))
        for name := range atys {
            names = append(names, name)
        }
        sort.Strings(names)

        items := make([]hclwrite.ObjectAttrTokens, len(names))
        for i, name := range names {
            items[i] = hclwrite.ObjectAttrTokens{
                Name:  hclwrite.TokensForIdentifier(name),
                Value: typeExprTokens(atys[name]),
            }
        }

        return hclwrite.TokensForObject(items)
    }

    if ty.IsTupleType() {
        etys := ty.TupleElementTypes()
        items := make([]hclwrite.Tokens, len(etys))
        for i, ety := range etys {
            items[i] = typeExprTokens(ety)
        }
        return hclwrite.TokensForTuple(items)
    }

    panic(fmt.Errorf("unsupported type %#v", ty))
}

This program will generate the same output as the previous example. You can change func main to pass a different type to typeExprTokens to see how it behaves with some different types.

Martin Atkins
  • 62,420
  • 8
  • 120
  • 138
  • Awesome, thank you soo sooo much. This code works like a charm and it has helped me a great deal to overcome a blocker. Thanks a ton, once again. – Mohammed Saaduddin Ansari Nov 08 '22 at 09:02
  • @MohammedSaaduddinAnsari As I predicted: Mart wrote a better answer than I would have attempted. Please upvote and accept it if was helpful to you (as you seem to indicate it was). – Matthew Schuchard Nov 08 '22 at 20:02
  • @MattSchuchard , Yes it helped me alot. Thanks Mart for such great answer. However, I still am stuck with another thing - where I need to figure out the type of each attribute and associate it with the cty.Type . I have posted another question : https://stackoverflow.com/questions/74378542/fetch-attribute-type-from-terraform-provider-schema Is it possible for you guys to take a look please. I really need help on this. – Mohammed Saaduddin Ansari Nov 09 '22 at 16:52