2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2020-09-03 11:34:04 +00:00
|
|
|
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
|
|
|
|
}
|