github.com/bigcommerce/nomad@v0.9.3-bc/helper/pluginutils/hclutils/testing.go (about) 1 package hclutils 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/hcl" 7 "github.com/hashicorp/hcl/hcl/ast" 8 "github.com/hashicorp/nomad/helper/pluginutils/hclspecutils" 9 "github.com/hashicorp/nomad/nomad/structs" 10 "github.com/hashicorp/nomad/plugins/drivers" 11 "github.com/hashicorp/nomad/plugins/shared/hclspec" 12 "github.com/mitchellh/mapstructure" 13 "github.com/stretchr/testify/require" 14 "github.com/ugorji/go/codec" 15 "github.com/zclconf/go-cty/cty" 16 ) 17 18 type HCLParser struct { 19 spec *hclspec.Spec 20 vars map[string]cty.Value 21 } 22 23 // NewConfigParser return a helper for parsing drivers TaskConfig 24 // Parser is an immutable object can be used in multiple tests 25 func NewConfigParser(spec *hclspec.Spec) *HCLParser { 26 return &HCLParser{ 27 spec: spec, 28 } 29 } 30 31 // WithVars returns a new parser that uses passed vars when interpolated strings in config 32 func (b *HCLParser) WithVars(vars map[string]cty.Value) *HCLParser { 33 return &HCLParser{ 34 spec: b.spec, 35 vars: vars, 36 } 37 } 38 39 // ParseJson parses the json config string and decode it into the `out` parameter. 40 // out parameter should be a golang reference to a driver specific TaskConfig reference. 41 // The function terminates and reports errors if any is found during conversion. 42 // 43 // Sample invocation would be 44 // 45 // ``` 46 // var tc *TaskConfig 47 // hclutils.NewConfigParser(spec).ParseJson(t, configString, &tc) 48 // ``` 49 func (b *HCLParser) ParseJson(t *testing.T, configStr string, out interface{}) { 50 config := JsonConfigToInterface(t, configStr) 51 b.parse(t, config, out) 52 } 53 54 // ParseHCL parses the hcl config string and decode it into the `out` parameter. 55 // out parameter should be a golang reference to a driver specific TaskConfig reference. 56 // The function terminates and reports errors if any is found during conversion. 57 // 58 // Sample invocation would be 59 // 60 // ``` 61 // var tc *TaskConfig 62 // hclutils.NewConfigParser(spec).ParseHCL(t, configString, &tc) 63 // ``` 64 func (b *HCLParser) ParseHCL(t *testing.T, configStr string, out interface{}) { 65 config := HclConfigToInterface(t, configStr) 66 b.parse(t, config, out) 67 } 68 69 func (b *HCLParser) parse(t *testing.T, config, out interface{}) { 70 decSpec, diags := hclspecutils.Convert(b.spec) 71 require.Empty(t, diags) 72 73 ctyValue, diag := ParseHclInterface(config, decSpec, b.vars) 74 require.Empty(t, diag) 75 76 // encode 77 dtc := &drivers.TaskConfig{} 78 require.NoError(t, dtc.EncodeDriverConfig(ctyValue)) 79 80 // decode 81 require.NoError(t, dtc.DecodeDriverConfig(out)) 82 } 83 84 func HclConfigToInterface(t *testing.T, config string) interface{} { 85 t.Helper() 86 87 // Parse as we do in the jobspec parser 88 root, err := hcl.Parse(config) 89 if err != nil { 90 t.Fatalf("failed to hcl parse the config: %v", err) 91 } 92 93 // Top-level item should be a list 94 list, ok := root.Node.(*ast.ObjectList) 95 if !ok { 96 t.Fatalf("root should be an object") 97 } 98 99 var m map[string]interface{} 100 if err := hcl.DecodeObject(&m, list.Items[0]); err != nil { 101 t.Fatalf("failed to decode object: %v", err) 102 } 103 104 var m2 map[string]interface{} 105 if err := mapstructure.WeakDecode(m, &m2); err != nil { 106 t.Fatalf("failed to weak decode object: %v", err) 107 } 108 109 return m2["config"] 110 } 111 112 func JsonConfigToInterface(t *testing.T, config string) interface{} { 113 t.Helper() 114 115 // Decode from json 116 dec := codec.NewDecoderBytes([]byte(config), structs.JsonHandle) 117 118 var m map[string]interface{} 119 err := dec.Decode(&m) 120 if err != nil { 121 t.Fatalf("failed to decode: %v", err) 122 } 123 124 return m["Config"] 125 }