github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/helper/pluginutils/hclutils/testing.go (about) 1 package hclutils 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/go-msgpack/codec" 7 "github.com/hashicorp/hcl" 8 "github.com/hashicorp/hcl/hcl/ast" 9 "github.com/hashicorp/nomad/helper/pluginutils/hclspecutils" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/plugins/drivers" 12 "github.com/hashicorp/nomad/plugins/shared/hclspec" 13 "github.com/mitchellh/mapstructure" 14 "github.com/stretchr/testify/require" 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, errs := ParseHclInterface(config, decSpec, b.vars) 74 require.Nil(t, errs) 75 require.Empty(t, diag) 76 77 // encode 78 dtc := &drivers.TaskConfig{} 79 require.NoError(t, dtc.EncodeDriverConfig(ctyValue)) 80 81 // decode 82 require.NoError(t, dtc.DecodeDriverConfig(out)) 83 } 84 85 func HclConfigToInterface(t *testing.T, config string) interface{} { 86 t.Helper() 87 88 // Parse as we do in the jobspec parser 89 root, err := hcl.Parse(config) 90 if err != nil { 91 t.Fatalf("failed to hcl parse the config: %v", err) 92 } 93 94 // Top-level item should be a list 95 list, ok := root.Node.(*ast.ObjectList) 96 if !ok { 97 t.Fatalf("root should be an object") 98 } 99 100 var m map[string]interface{} 101 if err := hcl.DecodeObject(&m, list.Items[0]); err != nil { 102 t.Fatalf("failed to decode object: %v", err) 103 } 104 105 var m2 map[string]interface{} 106 if err := mapstructure.WeakDecode(m, &m2); err != nil { 107 t.Fatalf("failed to weak decode object: %v", err) 108 } 109 110 return m2["config"] 111 } 112 113 func JsonConfigToInterface(t *testing.T, config string) interface{} { 114 t.Helper() 115 116 // Decode from json 117 dec := codec.NewDecoderBytes([]byte(config), structs.JsonHandle) 118 119 var m map[string]interface{} 120 err := dec.Decode(&m) 121 if err != nil { 122 t.Fatalf("failed to decode: %v", err) 123 } 124 125 return m["Config"] 126 }