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