Home > OS >  Get the type of value using cty in hclwrite
Get the type of value using cty in hclwrite

Time:11-10

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.

CodePudding user response:

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.

  • Related