Skip to content

Commit

Permalink
Rewrites hcl transformation logic into another package
Browse files Browse the repository at this point in the history
  • Loading branch information
sebradloff committed Apr 21, 2020
1 parent c423f92 commit ee6bb03
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 0 deletions.
85 changes: 85 additions & 0 deletions pkg/hcl/hcl_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package hcl

import (
"fmt"
"os"
"strings"

"github.com/ghodss/yaml"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl/v2/hclwrite"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

type HCLFile struct {
file *hclwrite.File
rootBody *hclwrite.Body
}

func NewHCLFile() *HCLFile {
f := hclwrite.NewEmptyFile()
return &HCLFile{
file: f,
rootBody: f.Body(),
}
}

func (f *HCLFile) GetFileBytes() []byte {
return f.file.Bytes()
}

func (f *HCLFile) GetFileRootBody() *hclwrite.Body {
return f.rootBody
}

func (f *HCLFile) K8sObjectToResourceBlock(o *unstructured.Unstructured) error {
oJSON, err := o.MarshalJSON()
if err != nil {
return fmt.Errorf("Failed to marshall one object into json: %v", err)
}
oYaml, err := yaml.JSONToYAML(oJSON)
if err != nil {
return fmt.Errorf("Failed to marshall one object json into yaml: %v", err)
}

ns := o.GetNamespace()
if ns == "" {
ns = "default"
}
groupVersion := strings.Replace(o.GetAPIVersion(), "/", "_", -1)
resourceName := strings.Join([]string{ns, groupVersion, o.GetKind(), o.GetName()}, "-")

contentBytes := []byte("<<EOT\n")
contentBytes = append(contentBytes, oYaml...)
contentBytes = append(contentBytes, []byte("EOT\n")...)

// create tf resource block
resourceBlock := f.rootBody.AppendNewBlock("resource", []string{"k8s_manifest", resourceName})

tokens := hclwrite.Tokens{
{
Type: hclsyntax.TokenOHeredoc,
Bytes: contentBytes,
},
}

bT := resourceBlock.Body().BuildTokens(tokens)
resourceBlock.Body().SetAttributeRaw("content", bT)
f.rootBody.AppendNewline()

return nil
}

func (hF *HCLFile) WriteToFile(path string) error {
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("could not os.Create(%s); err = %v", path, err)
}
defer f.Close()

_, err = hF.file.WriteTo(f)
if err != nil {
return fmt.Errorf("could not write to file %s; err = %v", f.Name(), err)
}
return nil
}
94 changes: 94 additions & 0 deletions pkg/hcl/hcl_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package hcl_test

import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"path/filepath"
"testing"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclwrite"
h "github.com/sebradloff/rawk8stfc/pkg/hcl"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)

var (
updateFlag bool
)

func init() {
flag.BoolVar(&updateFlag, "update", false, "Set this flag to update the golden files.")
}

func TestHCLFile_K8sObjectToResourceBlock(t *testing.T) {
// given
f := h.NewHCLFile()
var o unstructured.Unstructured
o.SetAPIVersion("apps/v1")
o.SetKind("Deployment")
o.SetName("test-1")
o.SetNamespace("test-1")
//when
err := f.K8sObjectToResourceBlock(&o)
if err != nil {
t.Fatalf("asdfsdf %v", err)
}

//then
goldenFile := filepath.Join("test-fixtures", fmt.Sprintf("%s.hcl", "test-1"))
if updateFlag {
err := f.WriteToFile(goldenFile)
if err != nil {
t.Fatalf("could not update golden file %s; err = %v", goldenFile, err)
}
}

wantBytes, err := ioutil.ReadFile(goldenFile)
if err != nil {
t.Fatalf("failed to read the goldenFile file: %s. err = %v", goldenFile, err)
}

wF, diags := hclwrite.ParseConfig(wantBytes, goldenFile, hcl.InitialPos)
if diags.HasErrors() {
for _, diag := range diags {
if diag.Subject != nil {
fmt.Printf("[%s:%d] %s: %s", diag.Subject.Filename, diag.Subject.Start.Line, diag.Summary, diag.Detail)
} else {
fmt.Printf("%s: %s", diag.Summary, diag.Detail)
}
}
}

f.GetFileRootBody()

if len(f.GetFileRootBody().Blocks()) != 1 {
t.Fatalf("got more than one block %d; want %d", len(f.GetFileRootBody().Blocks()), 1)
}

for _, block := range f.GetFileRootBody().Blocks() {
wantType := "resource"
if block.Type() != wantType {
t.Errorf("block type = %s; want %s", block.Type(), wantType)
}

if len(block.Labels()) != 2 {
t.Fatalf("block labels len(%d); want 2", len(block.Labels()))
}
wantFirstLabel := "k8s_manifest"
if block.Labels()[0] != wantFirstLabel {
t.Errorf("first block label = %s; want %s", block.Labels()[0], wantFirstLabel)
}

wantAttr := "content"
attr := block.Body().GetAttribute(wantAttr)
if attr == nil {
t.Errorf("got no body attribue; want attribute %s", wantAttr)
}
}

if !bytes.Equal(f.GetFileBytes(), wF.Bytes()) {
t.Errorf("the file bytes do not match the golden file bytes (%s)", goldenFile)
}
}
80 changes: 80 additions & 0 deletions pkg/hcl/internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package hcl

import (
"fmt"
"os"
"path/filepath"
"strconv"
"testing"

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

func Test_HCLFile_WriteToFile(t *testing.T) {
type checkFn func(*testing.T, *HCLFile, string, error)
check := func(fns ...checkFn) []checkFn { return fns }

hasNoErr := func() checkFn {
return func(t *testing.T, f *HCLFile, outputFilePath string, err error) {
if err != nil {
t.Fatalf("err = %v; want nil", err)
}
}
}

fileExistsWithRightNumOfBytes := func() checkFn {
return func(t *testing.T, f *HCLFile, outputFilePath string, err error) {
oF, err := os.Stat(outputFilePath)
if err != nil {
t.Fatalf("error checking on file %s; err = %v", outputFilePath, oF)
}

if !oF.Mode().IsRegular() {
t.Fatalf("%s is not a file", outputFilePath)
}

if int64(len(f.GetFileBytes())) != oF.Size() {
t.Errorf("the created file (%s) does not have the same number of bytes as the HCLFile", outputFilePath)
}
}
}

tests := map[string]struct {
numOfResources int
checks []checkFn
}{
"one resource": {
numOfResources: 1,
checks: check(hasNoErr(), fileExistsWithRightNumOfBytes()),
},
"two resources": {
numOfResources: 2,
checks: check(hasNoErr(), fileExistsWithRightNumOfBytes()),
},
"zero resources": {
numOfResources: 0,
checks: check(hasNoErr(), fileExistsWithRightNumOfBytes()),
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
hf := hclwrite.NewEmptyFile()
f := &HCLFile{
file: hf,
rootBody: hf.Body(),
}

for i := 0; i < tc.numOfResources; i++ {
f.file.Body().AppendNewBlock("test", []string{"t1", strconv.Itoa(i)})
}

outputFilePath := filepath.Join(os.TempDir(), fmt.Sprintf("%s.hcl", name))

err := f.WriteToFile(outputFilePath)
for _, check := range tc.checks {
check(t, f, outputFilePath, err)
}
})
}
}
11 changes: 11 additions & 0 deletions pkg/hcl/test-fixtures/test-1.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "k8s_manifest" "test-1-apps_v1-Deployment-test-1" {
content = <<EOT
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-1
namespace: test-1
EOT

}

0 comments on commit ee6bb03

Please sign in to comment.