-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
35 changed files
with
1,778 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [master] | ||
pull_request: | ||
branches: [master] | ||
|
||
concurrency: | ||
group: "${{ github.workflow }}-${{ github.head_ref }}" | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
ci: | ||
runs-on: ubuntu | ||
steps: | ||
- uses: actions/checkout@v3 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v3 | ||
with: | ||
go-version: 1.18 | ||
|
||
- name: Verify dependencies | ||
run: go mod verify | ||
|
||
- name: Install dependencies | ||
run: go mod download | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Run go vet | ||
run: go vet ./... | ||
|
||
- name: Install staticcheck | ||
run: go install honnef.co/go/tools/cmd/staticcheck@latest | ||
|
||
- name: Run staticcheck | ||
run: staticcheck ./... | ||
|
||
- name: Install golint | ||
run: go install golang.org/x/lint/golint@latest | ||
|
||
- name: Run golint | ||
run: golint ./... | ||
|
||
- name: Run tests | ||
run: go test -race -vet=off ./... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package tfsig | ||
|
||
import ( | ||
"github.com/hashicorp/hcl/v2/hclwrite" | ||
"github.com/zclconf/go-cty/cty" | ||
|
||
"github.com/yoanm/tfsig/tokens" | ||
) | ||
|
||
/** Public **/ | ||
|
||
func NewSignature(name string, labels []string, elements BodyElements) *BlockSignature { | ||
return &BlockSignature{ | ||
typeName: name, | ||
labels: labels, | ||
elements: elements, | ||
} | ||
} | ||
func NewEmptySignature(name string, labels ...string) *BlockSignature { | ||
return NewSignature(name, labels, BodyElements{}) | ||
} | ||
|
||
func NewEmptyResource(name, id string, labels ...string) *BlockSignature { | ||
return NewEmptySignature("resource", append([]string{name, id}, labels...)...) | ||
} | ||
|
||
type BlockSignature struct { | ||
typeName string | ||
labels []string | ||
elements BodyElements | ||
} | ||
|
||
func (signature *BlockSignature) GetType() string { | ||
return signature.typeName | ||
} | ||
func (signature *BlockSignature) GetLabels() []string { | ||
return signature.labels | ||
} | ||
func (signature *BlockSignature) GetElements() BodyElements { | ||
return signature.elements | ||
} | ||
func (signature *BlockSignature) SetElements(elements BodyElements) { | ||
signature.elements = elements | ||
} | ||
func (signature *BlockSignature) AppendElement(element BodyElement) { | ||
signature.elements = append(signature.elements, element) | ||
} | ||
|
||
func (signature *BlockSignature) AppendAttribute(name string, value cty.Value) { | ||
signature.AppendElement(NewBodyAttribute(name, value)) | ||
} | ||
func (signature *BlockSignature) AppendChild(child *BlockSignature) { | ||
signature.AppendElement(NewBodyBlock(child)) | ||
} | ||
func (signature *BlockSignature) AppendEmptyLine() { | ||
signature.AppendElement(NewBodyEmptyLine()) | ||
} | ||
|
||
func (signature *BlockSignature) Build() *hclwrite.Block { | ||
block := hclwrite.NewBlock(signature.GetType(), signature.GetLabels()) | ||
|
||
signature.WriteElementsToBody(block.Body()) | ||
|
||
return block | ||
} | ||
|
||
func (signature *BlockSignature) BuildTokens() (tks hclwrite.Tokens) { | ||
if block := signature.Build(); block != nil { | ||
blockTks := block.BuildTokens(nil) | ||
// Remove trailing new line automatically added (=remove last token) | ||
tks = append(hclwrite.Tokens{}, blockTks[0:len(blockTks)-1]...) | ||
} | ||
|
||
return tks | ||
} | ||
|
||
func (signature *BlockSignature) WriteElementsToBody(body *hclwrite.Body) { | ||
for _, value := range signature.GetElements() { | ||
if value.IsBodyBlock() { | ||
body.AppendBlock(value.Build()) | ||
} else if value.IsBodyAttribute() { | ||
if tokens.ContainsCapsule(value.attr) { | ||
body.SetAttributeRaw(value.GetName(), tokens.Generate(value.attr)) | ||
} else { | ||
body.SetAttributeValue(value.GetName(), *value.attr) | ||
} | ||
} else if value.IsBodyEmptyLine() { | ||
body.AppendNewline() | ||
} | ||
} | ||
} | ||
|
||
// DependsOn adds an empty line and the 'depends_on terraform directive with provided id list | ||
func (s *BlockSignature) DependsOn(idList []string) { | ||
if idList == nil { | ||
return | ||
} | ||
|
||
s.AppendEmptyLine() | ||
s.AppendAttribute("depends_on", *tokens.NewIdentListValue(idList)) | ||
} | ||
|
||
type LifecycleCondition struct { | ||
condition string | ||
errorMessage string | ||
} | ||
type LifecycleConfig struct { | ||
CreateBeforeDestroy *bool | ||
PreventDestroy *bool | ||
IgnoreChanges []string | ||
ReplaceTriggeredBy []string | ||
Precondition *LifecycleCondition | ||
Postcondition *LifecycleCondition | ||
} | ||
|
||
// Lifecycle adds an empty line and the 'lifecycle terraform directive, or return the existing signature | ||
func (s *BlockSignature) Lifecycle(config LifecycleConfig) { | ||
sig := NewEmptySignature("lifecycle") | ||
|
||
appendLifecycleBoolAttribute(sig, "create_before_destroy", config.CreateBeforeDestroy) | ||
appendLifecycleBoolAttribute(sig, "prevent_destroy", config.PreventDestroy) | ||
|
||
if config.IgnoreChanges != nil { | ||
sig.AppendAttribute("ignore_changes", *tokens.NewIdentListValue(config.IgnoreChanges)) | ||
} | ||
|
||
appendLifecycleConditionBlock(sig, "precondition", config.Precondition) | ||
appendLifecycleConditionBlock(sig, "postcondition", config.Postcondition) | ||
|
||
s.AppendEmptyLine() | ||
s.AppendChild(sig) | ||
} | ||
|
||
/** Private **/ | ||
|
||
func appendLifecycleConditionBlock(lifecycleSig *BlockSignature, name string, c *LifecycleCondition) { | ||
if c == nil { | ||
return | ||
} | ||
cond := NewEmptySignature(name) | ||
|
||
cond.AppendAttribute("condition", *tokens.NewIdentValue(c.condition)) | ||
cond.AppendAttribute("error_message", *tokens.NewIdentValue(c.errorMessage)) | ||
|
||
lifecycleSig.AppendChild(cond) | ||
} | ||
|
||
func appendLifecycleBoolAttribute(lifecycleSig *BlockSignature, name string, value *bool) { | ||
if value == nil { | ||
return | ||
} | ||
val := "false" | ||
if *value { | ||
val = "true" | ||
} | ||
lifecycleSig.AppendAttribute(name, *tokens.NewIdentValue(val)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package tfsig | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/hashicorp/hcl/v2/hclwrite" | ||
"github.com/zclconf/go-cty/cty" | ||
) | ||
|
||
func Example() { | ||
// Create a resource block | ||
sig := NewEmptyResource("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 | ||
// | ||
// } | ||
} | ||
|
||
func Example_second() { | ||
// Enhance an existing signature | ||
sig := NewEmptyResource("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 := BodyElements{} | ||
for _, v := range sig.GetElements() { | ||
// Remove all empty lines | ||
if !v.IsBodyEmptyLine() { | ||
newElems = append(newElems, v) | ||
} | ||
// Append a new attribute right after attribute2 | ||
if v.GetName() == "attribute2" { | ||
newElems = append(newElems, 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 | ||
// } | ||
} | ||
|
||
func Example_third() { | ||
sig := NewEmptyResource("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(BodyElements, len(sig.GetElements())) | ||
extraElemCont := 0 | ||
for _, v := range sig.GetElements() { | ||
if v.GetName() == "attribute1" { | ||
newElems[2] = v | ||
} else if v.GetName() == "attribute4" { | ||
newElems[1] = v | ||
} else if v.GetName() == "attribute3" { | ||
newElems[0] = v | ||
} else if !v.IsBodyEmptyLine() { | ||
newElems[3+extraElemCont] = v | ||
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" | ||
// } | ||
} |
Oops, something went wrong.