af8cab3d74
This eases adoption of the jobspec package by other projects (e.g. terraform nomad provider, Lavant). Either by consuming directy as a library (hopefully without having go mod import rest of nomad) or by copying the package without modification. Ideally, this package will be published as an independent module. We aren't ready for that considering we'll be switching to HCLv2 "soon", but eitherway, this seems like a reasonable intermediate step if we choose to.
114 lines
2.4 KiB
Go
114 lines
2.4 KiB
Go
package jobspec
|
|
|
|
// These functions are copied from helper/funcs.go
|
|
// added here to avoid jobspec depending on any other package
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
|
"github.com/hashicorp/hcl/hcl/ast"
|
|
)
|
|
|
|
// stringToPtr returns the pointer to a string
|
|
func stringToPtr(str string) *string {
|
|
return &str
|
|
}
|
|
|
|
// timeToPtr returns the pointer to a time.Duration.
|
|
func timeToPtr(t time.Duration) *time.Duration {
|
|
return &t
|
|
}
|
|
|
|
// boolToPtr returns the pointer to a boolean
|
|
func boolToPtr(b bool) *bool {
|
|
return &b
|
|
}
|
|
|
|
func checkHCLKeys(node ast.Node, valid []string) error {
|
|
var list *ast.ObjectList
|
|
switch n := node.(type) {
|
|
case *ast.ObjectList:
|
|
list = n
|
|
case *ast.ObjectType:
|
|
list = n.List
|
|
default:
|
|
return fmt.Errorf("cannot check HCL keys of type %T", n)
|
|
}
|
|
|
|
validMap := make(map[string]struct{}, len(valid))
|
|
for _, v := range valid {
|
|
validMap[v] = struct{}{}
|
|
}
|
|
|
|
var result error
|
|
for _, item := range list.Items {
|
|
key := item.Keys[0].Token.Value().(string)
|
|
if _, ok := validMap[key]; !ok {
|
|
result = multierror.Append(result, fmt.Errorf(
|
|
"invalid key: %s", key))
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// UnusedKeys returns a pretty-printed error if any `hcl:",unusedKeys"` is not empty
|
|
func unusedKeys(obj interface{}) error {
|
|
val := reflect.ValueOf(obj)
|
|
if val.Kind() == reflect.Ptr {
|
|
val = reflect.Indirect(val)
|
|
}
|
|
return unusedKeysImpl([]string{}, val)
|
|
}
|
|
|
|
func unusedKeysImpl(path []string, val reflect.Value) error {
|
|
stype := val.Type()
|
|
for i := 0; i < stype.NumField(); i++ {
|
|
ftype := stype.Field(i)
|
|
fval := val.Field(i)
|
|
tags := strings.Split(ftype.Tag.Get("hcl"), ",")
|
|
name := tags[0]
|
|
tags = tags[1:]
|
|
|
|
if fval.Kind() == reflect.Ptr {
|
|
fval = reflect.Indirect(fval)
|
|
}
|
|
|
|
// struct? recurse. Add the struct's key to the path
|
|
if fval.Kind() == reflect.Struct {
|
|
err := unusedKeysImpl(append([]string{name}, path...), fval)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
// Search the hcl tags for "unusedKeys"
|
|
unusedKeys := false
|
|
for _, p := range tags {
|
|
if p == "unusedKeys" {
|
|
unusedKeys = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if unusedKeys {
|
|
ks, ok := fval.Interface().([]string)
|
|
if ok && len(ks) != 0 {
|
|
ps := ""
|
|
if len(path) > 0 {
|
|
ps = strings.Join(path, ".") + " "
|
|
}
|
|
return fmt.Errorf("%sunexpected keys %s",
|
|
ps,
|
|
strings.Join(ks, ", "))
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|