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  }