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  }