github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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 if len(errs) > 1 { 75 t.Error("unexpected errors parsing file") 76 for _, err := range errs { 77 t.Errorf(" * %v", err) 78 79 } 80 t.FailNow() 81 } 82 require.Empty(t, diag) 83 84 // encode 85 dtc := &drivers.TaskConfig{} 86 require.NoError(t, dtc.EncodeDriverConfig(ctyValue)) 87 88 // decode 89 require.NoError(t, dtc.DecodeDriverConfig(out)) 90 } 91 92 func HclConfigToInterface(t *testing.T, config string) interface{} { 93 t.Helper() 94 95 // Parse as we do in the jobspec parser 96 root, err := hcl.Parse(config) 97 if err != nil { 98 t.Fatalf("failed to hcl parse the config: %v", err) 99 } 100 101 // Top-level item should be a list 102 list, ok := root.Node.(*ast.ObjectList) 103 if !ok { 104 t.Fatalf("root should be an object") 105 } 106 107 var m map[string]interface{} 108 if err := hcl.DecodeObject(&m, list.Items[0]); err != nil { 109 t.Fatalf("failed to decode object: %v", err) 110 } 111 112 var m2 map[string]interface{} 113 if err := mapstructure.WeakDecode(m, &m2); err != nil { 114 t.Fatalf("failed to weak decode object: %v", err) 115 } 116 117 return m2["config"] 118 } 119 120 func JsonConfigToInterface(t *testing.T, config string) interface{} { 121 t.Helper() 122 123 // Decode from json 124 dec := codec.NewDecoderBytes([]byte(config), structs.JsonHandle) 125 126 var m map[string]interface{} 127 err := dec.Decode(&m) 128 if err != nil { 129 t.Fatalf("failed to decode: %v", err) 130 } 131 132 return m["Config"] 133 }