github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/helper/pluginutils/hclutils/util.go (about) 1 package hclutils 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 "github.com/hashicorp/go-msgpack/codec" 9 hcl "github.com/hashicorp/hcl/v2" 10 "github.com/hashicorp/hcl/v2/hcldec" 11 hjson "github.com/hashicorp/hcl/v2/json" 12 13 "github.com/hashicorp/nomad/nomad/structs" 14 15 "github.com/zclconf/go-cty/cty" 16 "github.com/zclconf/go-cty/cty/function" 17 "github.com/zclconf/go-cty/cty/function/stdlib" 18 ) 19 20 // ParseHclInterface is used to convert an interface value representing a hcl2 21 // body and return the interpolated value. Vars may be nil if there are no 22 // variables to interpolate. 23 func ParseHclInterface(val interface{}, spec hcldec.Spec, vars map[string]cty.Value) (cty.Value, hcl.Diagnostics, []error) { 24 evalCtx := &hcl.EvalContext{ 25 Variables: vars, 26 Functions: GetStdlibFuncs(), 27 } 28 29 // Encode to json 30 var buf bytes.Buffer 31 enc := codec.NewEncoder(&buf, structs.JsonHandle) 32 err := enc.Encode(val) 33 if err != nil { 34 // Convert to a hcl diagnostics message 35 errorMessage := fmt.Sprintf("Label encoding failed: %v", err) 36 return cty.NilVal, 37 hcl.Diagnostics([]*hcl.Diagnostic{{ 38 Severity: hcl.DiagError, 39 Summary: "Failed to encode label value", 40 Detail: errorMessage, 41 }}), 42 []error{errors.New(errorMessage)} 43 } 44 45 // Parse the json as hcl2 46 hclFile, diag := hjson.Parse(buf.Bytes(), "") 47 if diag.HasErrors() { 48 return cty.NilVal, diag, formattedDiagnosticErrors(diag) 49 } 50 51 value, decDiag := hcldec.Decode(hclFile.Body, spec, evalCtx) 52 diag = diag.Extend(decDiag) 53 if diag.HasErrors() { 54 return cty.NilVal, diag, formattedDiagnosticErrors(diag) 55 } 56 57 return value, diag, nil 58 } 59 60 // GetStdlibFuncs returns the set of stdlib functions. 61 func GetStdlibFuncs() map[string]function.Function { 62 return map[string]function.Function{ 63 "abs": stdlib.AbsoluteFunc, 64 "coalesce": stdlib.CoalesceFunc, 65 "concat": stdlib.ConcatFunc, 66 "hasindex": stdlib.HasIndexFunc, 67 "int": stdlib.IntFunc, 68 "jsondecode": stdlib.JSONDecodeFunc, 69 "jsonencode": stdlib.JSONEncodeFunc, 70 "length": stdlib.LengthFunc, 71 "lower": stdlib.LowerFunc, 72 "max": stdlib.MaxFunc, 73 "min": stdlib.MinFunc, 74 "reverse": stdlib.ReverseFunc, 75 "strlen": stdlib.StrlenFunc, 76 "substr": stdlib.SubstrFunc, 77 "upper": stdlib.UpperFunc, 78 } 79 } 80 81 // TODO: update hcl2 library with better diagnostics formatting for streamed configs 82 // - should be arbitrary labels not JSON https://github.com/hashicorp/hcl2/blob/4fba5e1a75e382aed7f7a7993f2c4836a5e1cd52/hcl/json/structure.go#L66 83 // - should not print diagnostic subject https://github.com/hashicorp/hcl2/blob/4fba5e1a75e382aed7f7a7993f2c4836a5e1cd52/hcl/diagnostic.go#L77 84 func formattedDiagnosticErrors(diag hcl.Diagnostics) []error { 85 var errs []error 86 for _, d := range diag { 87 if d.Summary == "Extraneous JSON object property" { 88 d.Summary = "Invalid label" 89 } 90 err := fmt.Errorf("%s: %s", d.Summary, d.Detail) 91 errs = append(errs, err) 92 } 93 return errs 94 }