tfsig

package module
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 27, 2024 License: MPL-2.0 Imports: 6 Imported by: 6

README

go-tfsig - hclwrite wrapper

License Code size Go Reference

Dependabot Status Last commit

Codacy Badge Go Report Card

CI codecov

GitHub go.mod Go version

Wrapper for Terraform HCL language (hclwrite)

Generate block signatures which are easier to manipulate and alter than hclwrite tokens

Documentation

Package documentation available there

Documentation

Overview

Package tfsig is a wrapper for Terraform HCL language (`hclwrite`).

It provides ability to generate block signature which are way easier to manipulate and alter than hclwrite.tokens type

Example
// Create a resource block
sig := tfsig.NewResource("res_name", "res_id")
sig.AppendAttribute("attribute1", cty.StringVal("value1"))
sig.AppendEmptyLine()
sig.AppendAttribute("attribute2", cty.BoolVal(true))
sig.AppendAttribute("attribute3", cty.NumberFloatVal(-12.34))
sig.AppendEmptyLine()

hclFile := hclwrite.NewEmptyFile()
hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "res_name" "res_id" {
  attribute1 = "value1"

  attribute2 = true
  attribute3 = -12.34

}
Example (Enhance_existing)
// Enhance an existing signature
sig := tfsig.NewResource("res_name", "res_id")
sig.AppendAttribute("attribute1", cty.StringVal("value1"))
sig.AppendEmptyLine()
sig.AppendAttribute("attribute2", cty.BoolVal(true))
sig.AppendAttribute("attribute3", cty.NumberFloatVal(-12.34))
sig.AppendEmptyLine()

// ... Later in the code
// Enhance signature by removing empty lines and add an attribute below attribute2
newElems := tfsig.BodyElements{}

for _, elem := range sig.GetElements() {
	// Remove all empty lines
	if !elem.IsBodyEmptyLine() {
		newElems = append(newElems, elem)
	}
	// Append a new attribute right after attribute2
	if elem.GetName() == "attribute2" {
		newElems = append(newElems, tfsig.NewBodyAttribute("attribute22", cty.BoolVal(false)))
	}
}

sig.SetElements(newElems)

// ... Finally
hclFile := hclwrite.NewEmptyFile()

hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "res_name" "res_id" {
  attribute1  = "value1"
  attribute2  = true
  attribute22 = false
  attribute3  = -12.34
}
Example (Reorder)
// Reorder an existing signature
sig := tfsig.NewResource("res_name", "res_id")
sig.AppendAttribute("attribute1", cty.StringVal("value1"))
sig.AppendAttribute("attribute2", cty.BoolVal(true))
sig.AppendAttribute("attribute3", cty.NumberFloatVal(-12.34))
sig.AppendEmptyLine()
sig.AppendAttribute("attribute4", cty.NumberIntVal(42))
sig.AppendAttribute("attribute5", cty.StringVal("value5"))

// ... Later in the code
// Re-order elements and remove empty lines
newElems := make(tfsig.BodyElements, len(sig.GetElements()))
extraElemCont := 0

for _, elem := range sig.GetElements() {
	switch {
	case elem.GetName() == "attribute1":
		newElems[2] = elem
	case elem.GetName() == "attribute4":
		newElems[1] = elem
	case elem.GetName() == "attribute3":
		newElems[0] = elem
	case !elem.IsBodyEmptyLine():
		newElems[3+extraElemCont] = elem
		extraElemCont++
	}
}

sig.SetElements(newElems)

hclFile := hclwrite.NewEmptyFile()
hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "res_name" "res_id" {
  attribute3 = -12.34
  attribute4 = 42
  attribute1 = "value1"
  attribute2 = true
  attribute5 = "value5"
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendAttributeIfNotNil added in v0.2.0

func AppendAttributeIfNotNil(sig *BlockSignature, attrName string, v *cty.Value)

AppendAttributeIfNotNil appends the provided attribute to the signature only if not nil

It simply avoids an `if` in your code.

Example
hclFile := hclwrite.NewEmptyFile()
evenFn := func(i int) *cty.Value {
	if i%2 == 0 {
		return nil
	}

	val := cty.StringVal(fmt.Sprintf("val%d", i))

	return &val
}

sig := tfsig.NewResource("my_res", "res_id")

tfsig.AppendAttributeIfNotNil(sig, "attr_0", evenFn(0))
tfsig.AppendAttributeIfNotNil(sig, "attr_1", evenFn(1))
tfsig.AppendAttributeIfNotNil(sig, "attr_2", evenFn(2))
tfsig.AppendAttributeIfNotNil(sig, "attr_3", evenFn(3))
tfsig.AppendAttributeIfNotNil(sig, "attr_4", evenFn(4))

hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "my_res" "res_id" {
  attr_1 = "val1"
  attr_3 = "val3"
}

func AppendBlockIfNotNil

func AppendBlockIfNotNil(body *hclwrite.Body, block *hclwrite.Block)

AppendBlockIfNotNil appends the provided block to the provided body only if block is not nil

It simply avoids an `if` in your code.

Example
hclFile := hclwrite.NewEmptyFile()
evenFn := func(i int) *hclwrite.Block {
	if i%2 == 0 {
		return nil
	}

	return tfsig.NewSignature(fmt.Sprintf("block%d", i)).Build()
}

tfsig.AppendBlockIfNotNil(hclFile.Body(), evenFn(0))
tfsig.AppendBlockIfNotNil(hclFile.Body(), evenFn(1))
tfsig.AppendBlockIfNotNil(hclFile.Body(), evenFn(2))
tfsig.AppendBlockIfNotNil(hclFile.Body(), evenFn(3))
tfsig.AppendBlockIfNotNil(hclFile.Body(), evenFn(4))

fmt.Println(string(hclFile.Bytes()))
Output:

block1 {
}
block3 {
}

func AppendChildIfNotNil added in v0.2.0

func AppendChildIfNotNil(sig *BlockSignature, child *BlockSignature)

AppendChildIfNotNil appends the provided child to the signature only if not nil. And in case there is existing elements, it prepends an empty line

It simply avoids two `if` in your code.

Example
hclFile := hclwrite.NewEmptyFile()
oddFn := func(i int) *tfsig.BlockSignature {
	if i%2 != 0 {
		return nil
	}

	return tfsig.NewSignature(fmt.Sprintf("block%d", i))
}

sig := tfsig.NewResource("my_res", "res_id")

tfsig.AppendChildIfNotNil(sig, oddFn(0))
tfsig.AppendChildIfNotNil(sig, oddFn(1))
tfsig.AppendChildIfNotNil(sig, oddFn(2))
tfsig.AppendChildIfNotNil(sig, oddFn(3))
tfsig.AppendChildIfNotNil(sig, oddFn(4))

hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "my_res" "res_id" {
  block0 {
  }

  block2 {
  }

  block4 {
  }
}

func AppendNewLineAndBlockIfNotNil

func AppendNewLineAndBlockIfNotNil(body *hclwrite.Body, block *hclwrite.Block)

AppendNewLineAndBlockIfNotNil appends an empty line followed by provided block to the provided body only if block is not nil

It simply avoids an `if` in your code.

Example
hclFile := hclwrite.NewEmptyFile()
oddFn := func(i int) *hclwrite.Block {
	if i%2 != 0 {
		return nil
	}

	return tfsig.NewSignature(fmt.Sprintf("block%d", i)).Build()
}

tfsig.AppendNewLineAndBlockIfNotNil(hclFile.Body(), oddFn(0))
tfsig.AppendNewLineAndBlockIfNotNil(hclFile.Body(), oddFn(1))
tfsig.AppendNewLineAndBlockIfNotNil(hclFile.Body(), oddFn(2))
tfsig.AppendNewLineAndBlockIfNotNil(hclFile.Body(), oddFn(3))
tfsig.AppendNewLineAndBlockIfNotNil(hclFile.Body(), oddFn(4))

fmt.Println(string(hclFile.Bytes()))
Output:

block0 {
}

block2 {
}

block4 {
}

func ToTerraformIdentifier

func ToTerraformIdentifier(s string) string

ToTerraformIdentifier converts a string to a terraform identifier, by converting not allowed characters to `-`

And if provided value starts with a character not allowed as first character, it replaces it by `_`.

Example
fmt.Printf("a_valid-identifier becomes %s\n", tfsig.ToTerraformIdentifier("a_valid-identifier"))
fmt.Printf(".github becomes %s\n", tfsig.ToTerraformIdentifier(".github"))
fmt.Printf("an.identifier becomes %s\n", tfsig.ToTerraformIdentifier("an.identifier"))
fmt.Printf("0id becomes %s\n", tfsig.ToTerraformIdentifier("0id"))
Output:

a_valid-identifier becomes a_valid-identifier
.github becomes _github
an.identifier becomes an-identifier
0id becomes _id

Types

type BlockSignature

type BlockSignature struct {
	// contains filtered or unexported fields
}

BlockSignature is basically a wrapper to HCL blocks It holds a type, the block labels and its elements.

func NewResource added in v0.2.0

func NewResource(name, id string, labels ...string) *BlockSignature

NewResource returns a BlockSignature pointer with "resource" type and filled with provided labels.

func NewSignature

func NewSignature(name string, labels ...string) *BlockSignature

NewSignature returns a BlockSignature pointer filled with provided type and labels.

func (*BlockSignature) AppendAttribute

func (sig *BlockSignature) AppendAttribute(name string, value cty.Value)

AppendAttribute appends an attribute to the block.

func (*BlockSignature) AppendChild

func (sig *BlockSignature) AppendChild(child *BlockSignature)

AppendChild appends a child block to the block.

func (*BlockSignature) AppendElement

func (sig *BlockSignature) AppendElement(element BodyElement)

AppendElement appends an element to the block.

func (*BlockSignature) AppendEmptyLine

func (sig *BlockSignature) AppendEmptyLine()

AppendEmptyLine appends an empty line to the block.

func (*BlockSignature) Build

func (sig *BlockSignature) Build() *hclwrite.Block

Build creates a `hclwrite.Block` and appends block's elements to it.

func (*BlockSignature) BuildTokens

func (sig *BlockSignature) BuildTokens() hclwrite.Tokens

BuildTokens builds the block signature as `hclwrite.Tokens`.

func (*BlockSignature) DependsOn

func (sig *BlockSignature) DependsOn(idList []string)

DependsOn adds an empty line and the 'depends_on' terraform directive with provided id list.

Example
// resource with 'depends_on' directive
sig := tfsig.NewResource("res_name", "res_id")
sig.AppendAttribute("attribute1", cty.StringVal("value1"))
sig.DependsOn([]string{"another_res.res_id", "another_another_res.res_id"})

hclFile := hclwrite.NewEmptyFile()
hclFile.Body().AppendBlock(sig.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "res_name" "res_id" {
  attribute1 = "value1"

  depends_on = [another_res.res_id, another_another_res.res_id]
}

func (*BlockSignature) GetElements

func (sig *BlockSignature) GetElements() BodyElements

GetElements returns all elements attached to the block.

func (*BlockSignature) GetLabels

func (sig *BlockSignature) GetLabels() []string

GetLabels returns labels attached to the block.

func (*BlockSignature) GetType

func (sig *BlockSignature) GetType() string

GetType returns the type of the block.

func (*BlockSignature) Lifecycle

func (sig *BlockSignature) Lifecycle(config LifecycleConfig)

Lifecycle adds an empty line and the 'lifecycle' terraform directive and then append provided lifecycle attributes.

Example
// resource with 'lifecycle' directive
sig := tfsig.NewResource("res_name", "res_id")
sig.AppendAttribute("attribute1", cty.StringVal("value1"))

config := tfsig.LifecycleConfig{}
config.SetCreateBeforeDestroy(true)
config.SetPreventDestroy(false)
sig.Lifecycle(config)

sig2 := tfsig.NewResource("res2_name", "res2_id")
sig2.AppendAttribute("attribute1", cty.StringVal("value1"))

config2 := tfsig.LifecycleConfig{
	IgnoreChanges: []string{"attribute1"},
	Postcondition: &tfsig.LifecycleCondition{
		Condition:    "res_name.res_id.attribute1 != \"value1\"",
		ErrorMessage: "res_name.res_id.attribute1 must equal \"value1\"",
	},
}
sig2.Lifecycle(config2)

hclFile := hclwrite.NewEmptyFile()
hclFile.Body().AppendBlock(sig.Build())
hclFile.Body().AppendBlock(sig2.Build())

fmt.Println(string(hclFile.Bytes()))
Output:

resource "res_name" "res_id" {
  attribute1 = "value1"

  lifecycle {
    create_before_destroy = true
    prevent_destroy       = false
  }
}
resource "res2_name" "res2_id" {
  attribute1 = "value1"

  lifecycle {
    ignore_changes = [attribute1]
    postcondition {
      condition     = res_name.res_id.attribute1 != "value1"
      error_message = "res_name.res_id.attribute1 must equal \"value1\""
    }
  }
}

func (*BlockSignature) SetElements

func (sig *BlockSignature) SetElements(elements BodyElements)

SetElements overrides existing elements by provided ones.

type BodyElement

type BodyElement struct {
	// contains filtered or unexported fields
}

BodyElement is a wrapper for more or less anything that can be appended to a BlockSignature.

func NewBodyAttribute

func NewBodyAttribute(name string, attr cty.Value) BodyElement

NewBodyAttribute returns an Attribute BodyElement.

func NewBodyBlock

func NewBodyBlock(block *BlockSignature) BodyElement

NewBodyBlock returns a Block BodyElement.

func NewBodyEmptyLine

func NewBodyEmptyLine() BodyElement

NewBodyEmptyLine returns an empty line BodyElement.

func (BodyElement) Build

func (e BodyElement) Build() *hclwrite.Block

Build convert the current BodyElement into a `hclwrite.Block`

it panics if BodyElement is not a block (use `IsBodyBlock()` first).

func (BodyElement) GetBodyAttribute

func (e BodyElement) GetBodyAttribute() *cty.Value

GetBodyAttribute returns the value of the attribute behind the BodyElement

It panics if BodyElement is not an attribute (use `IsBodyAttribute()` first).

func (BodyElement) GetBodyBlock

func (e BodyElement) GetBodyBlock() *BlockSignature

GetBodyBlock returns the block behind the BodyElement

it panics if BodyElement is not a block (use `IsBodyBlock()` first).

func (BodyElement) GetName

func (e BodyElement) GetName() string

GetName returns the name of the BodyElement.

func (BodyElement) IsBodyAttribute

func (e BodyElement) IsBodyAttribute() bool

IsBodyAttribute returns true if the BodyElement is an attribute.

func (BodyElement) IsBodyBlock

func (e BodyElement) IsBodyBlock() bool

IsBodyBlock returns true if the BodyElement is a block.

func (BodyElement) IsBodyEmptyLine

func (e BodyElement) IsBodyEmptyLine() bool

IsBodyEmptyLine returns true if the BodyElement is an empty line.

type BodyElements

type BodyElements []BodyElement

BodyElements is a simple wrapper for a list of BodyElement.

type IdentTokenMatcher

type IdentTokenMatcher struct {
	// contains filtered or unexported fields
}

IdentTokenMatcher is a simple implementation for IdentTokenMatcherInterface.

func NewIdentTokenMatcher

func NewIdentTokenMatcher(prefixList ...string) IdentTokenMatcher

NewIdentTokenMatcher returns an instance of IdentTokenMatcher with provided list of prefix to consider as 'ident' tokens

`local.`, `var.` and `data.` tokens will be considered as 'ident' tokens by default.

func (IdentTokenMatcher) IsIdentToken

func (m IdentTokenMatcher) IsIdentToken(s string) bool

IsIdentToken is the implementation for IdentTokenMatcherInterface.

type IdentTokenMatcherInterface

type IdentTokenMatcherInterface interface {
	IsIdentToken(s string) bool
}

IdentTokenMatcherInterface is a simple interface declaring required method to detect an 'ident' token.

type LifecycleCondition

type LifecycleCondition struct {
	Condition    string
	ErrorMessage string
}

LifecycleCondition is used for Precondition and Postcondition property of LifecycleConfig It's basically a wrapper for terraform lifecycle pre- and post-conditions.

type LifecycleConfig

type LifecycleConfig struct {
	CreateBeforeDestroy *bool
	PreventDestroy      *bool
	IgnoreChanges       []string
	ReplaceTriggeredBy  []string
	Precondition        *LifecycleCondition
	Postcondition       *LifecycleCondition
}

LifecycleConfig is used as argument for `Lifecycle()` method It's basically a wrapper for terraform `lifecycle` directive.

func (*LifecycleConfig) SetCreateBeforeDestroy

func (c *LifecycleConfig) SetCreateBeforeDestroy(b bool)

SetCreateBeforeDestroy is a simple helper to avoid having to create a boolean variable and then pass the pointer to it

E.g: instead of writing

createBeforeDestroy = true
config := LifecycleConfig{CreateBeforeDestroy: &createBeforeDestroy}

Simply write:

config := LifecycleConfig{}
config.SetCreateBeforeDestroy(true)

func (*LifecycleConfig) SetPreventDestroy

func (c *LifecycleConfig) SetPreventDestroy(b bool)

SetPreventDestroy is a simple helper to avoid having to create a boolean variable and then pass the pointer to it

E.g: instead of writing

preventDestroy = false
config := LifecycleConfig{PreventDestroy: &preventDestroy}

Simply write:

config := LifecycleConfig{}
config.SetPreventDestroy(true)

type ValueGenerator

type ValueGenerator struct {
	// contains filtered or unexported fields
}

ValueGenerator is able to detect "ident" tokens and convert them into a special cty capsule. Capsule will then be converted to `hclwrite.tokens` It allows to write values like `var.my_var`, `locals.my_local` or `data.res_name.val_name` without any quotes.

Example
basicStringValue := "basic_value"
localVal := "local.my_local"
varVal := "var.my_var"
dataVal := "data.my_data.my_property"
customStringValue := "custom.my_var"
identStringValue := "explicit_ident.foo"
identListStringValue := []string{"explicit_ident_item.foo", "explicit_ident_item.bar"}

valGen := tfsig.NewValueGenerator()
sig := tfsig.NewSignature("my_block")
sig.AppendAttribute("attr1", *valGen.ToString(&basicStringValue))
sig.AppendAttribute("attr2", *valGen.ToString(&localVal))
sig.AppendAttribute("attr3", *valGen.ToString(&varVal))
sig.AppendAttribute("attr4", *valGen.ToString(&dataVal))
sig.AppendAttribute("attr5", *valGen.ToString(&customStringValue))
sig.AppendEmptyLine()
sig.AppendAttribute("attr6", *valGen.ToIdent(&identStringValue))
sig.AppendAttribute("attr7", *valGen.ToIdentList(&identListStringValue))

customValGen := tfsig.NewValueGenerator("custom.")

sig.AppendEmptyLine()
sig.AppendAttribute("attr8", *customValGen.ToString(&customStringValue))

hclFile := hclwrite.NewEmptyFile()
hclFile.Body().AppendBlock(sig.Build())
fmt.Println(string(hclFile.Bytes()))
Output:

my_block {
  attr1 = "basic_value"
  attr2 = local.my_local
  attr3 = var.my_var
  attr4 = data.my_data.my_property
  attr5 = "custom.my_var"

  attr6 = explicit_ident.foo
  attr7 = [explicit_ident_item.foo, explicit_ident_item.bar]

  attr8 = custom.my_var
}

func NewValueGenerator

func NewValueGenerator(identPrefixList ...string) ValueGenerator

NewValueGenerator returns a new ValueGenerator with the default 'ident' tokens matcher augmented with provided list of token to consider as 'ident' tokens.

func NewValueGeneratorWith

func NewValueGeneratorWith(matcher IdentTokenMatcherInterface) ValueGenerator

NewValueGeneratorWith returns a new ValueGenerator with the provided matcher.

func (*ValueGenerator) FromString

func (g *ValueGenerator) FromString(val *string, toType cty.Type) *cty.Value

FromString convert a string to `cty.Value` of the provided type If the provided string is actually an 'ident' token, `cty.Value` will be a capsule holding `hclwrite.tokens`.

func (*ValueGenerator) ToBool

func (g *ValueGenerator) ToBool(s *string) *cty.Value

ToBool convert a string to `cty.Value` boolean which will be rendered as true or false value by terraform HCL If the provided string is actually an 'ident' token, `cty.Value` will be a capsule holding `hclwrite.tokens`.

func (*ValueGenerator) ToIdent

func (g *ValueGenerator) ToIdent(s *string) *cty.Value

ToIdent converts a string to a special `cty.Value` capsule holding `hclwrite.tokens`.

func (*ValueGenerator) ToIdentList

func (g *ValueGenerator) ToIdentList(list *[]string) *cty.Value

ToIdentList converts a list of string to `cty.Value` list containing capsules holding `hclwrite.tokens`.

func (*ValueGenerator) ToNumber

func (g *ValueGenerator) ToNumber(s *string) *cty.Value

ToNumber convert a string to `cty.Value` number which will be rendered as numeric value by terraform HCL If the provided string is actually an 'ident' token, `cty.Value` will be a capsule holding `hclwrite.tokens`.

func (*ValueGenerator) ToString

func (g *ValueGenerator) ToString(s *string) *cty.Value

ToString convert a string to `cty.Value` string which will be rendered as quoted string by terraform HCL If the provided string is actually an 'ident' token, `cty.Value` will be a capsule holding `hclwrite.tokens`.

func (*ValueGenerator) ToStringList

func (g *ValueGenerator) ToStringList(list *[]string) *cty.Value

ToStringList convert a string list to `cty.Value` string list which will be rendered as quoted string list by terraform HCL. If a provided string item is actually an 'ident' token, `cty.Value` item will be a capsule holding `hclwrite.tokens`.

Directories

Path Synopsis
Package tokens provides an easy way to create common hclwrite tokens (such as new line, comma, equal sign, ident)
Package tokens provides an easy way to create common hclwrite tokens (such as new line, comma, equal sign, ident)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL