Isolate the jobspec package from the rest of Nomad (#8815)

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.
This commit is contained in:
Mahmood Ali
2020-09-03 06:34:04 -05:00
committed by GitHub
parent 884fe5c032
commit 9fe74c90f7
10 changed files with 470 additions and 330 deletions

113
jobspec/helper.go Normal file
View File

@@ -0,0 +1,113 @@
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
}

24
jobspec/helper_test.go Normal file
View File

@@ -0,0 +1,24 @@
package jobspec
// These functions are copied from helper/funcs.go
// added here to avoid jobspec depending on any other package
// intToPtr returns the pointer to an int
func intToPtr(i int) *int {
return &i
}
// int8ToPtr returns the pointer to an int8
func int8ToPtr(i int8) *int8 {
return &i
}
// int64ToPtr returns the pointer to an int
func int64ToPtr(i int64) *int64 {
return &i
}
// Uint64ToPtr returns the pointer to an uint64
func uint64ToPtr(u uint64) *uint64 {
return &u
}

View File

@@ -13,7 +13,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -48,7 +47,7 @@ func Parse(r io.Reader) (*api.Job, error) {
valid := []string{
"job",
}
if err := helper.CheckHCLKeys(list, valid); err != nil {
if err := checkHCLKeys(list, valid); err != nil {
return nil, err
}
@@ -100,7 +99,7 @@ func parseReschedulePolicy(final **api.ReschedulePolicy, list *ast.ObjectList) e
"max_delay",
"delay_function",
}
if err := helper.CheckHCLKeys(obj.Val, valid); err != nil {
if err := checkHCLKeys(obj.Val, valid); err != nil {
return err
}
@@ -140,7 +139,7 @@ func parseConstraints(result *[]*api.Constraint, list *ast.ObjectList) error {
"version",
"semver",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -230,7 +229,7 @@ func parseAffinities(result *[]*api.Affinity, list *ast.ObjectList) error {
"semver",
"weight",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -307,7 +306,7 @@ func parseSpread(result *[]*api.Spread, list *ast.ObjectList) error {
"weight",
"target",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -370,7 +369,7 @@ func parseSpreadTarget(result *[]*api.SpreadTarget, list *ast.ObjectList) error
"percent",
"value",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n))
}
@@ -433,7 +432,7 @@ func parseUpdate(result **api.UpdateStrategy, list *ast.ObjectList) error {
"auto_promote",
"canary",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -469,7 +468,7 @@ func parseMigrate(result **api.MigrateStrategy, list *ast.ObjectList) error {
"min_healthy_time",
"healthy_deadline",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -512,7 +511,7 @@ func parseVault(result *api.Vault, list *ast.ObjectList) error {
"change_mode",
"change_signal",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, "vault ->")
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -58,7 +57,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
"scaling",
"stop_after_client_disconnect",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n))
}
@@ -84,7 +83,7 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
// Build the group with the basic decode
var g api.TaskGroup
g.Name = helper.StringToPtr(n)
g.Name = stringToPtr(n)
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
WeaklyTypedInput: true,
@@ -201,8 +200,8 @@ func parseGroups(result *api.Job, list *ast.ObjectList) error {
// If we have a vault block, then parse that
if o := listVal.Filter("vault"); len(o.Items) > 0 {
tgVault := &api.Vault{
Env: helper.BoolToPtr(true),
ChangeMode: helper.StringToPtr("restart"),
Env: boolToPtr(true),
ChangeMode: stringToPtr("restart"),
}
if err := parseVault(tgVault, o); err != nil {
@@ -244,7 +243,7 @@ func parseEphemeralDisk(result **api.EphemeralDisk, list *ast.ObjectList) error
"size",
"migrate",
}
if err := helper.CheckHCLKeys(obj.Val, valid); err != nil {
if err := checkHCLKeys(obj.Val, valid); err != nil {
return err
}
@@ -278,7 +277,7 @@ func parseRestartPolicy(final **api.RestartPolicy, list *ast.ObjectList) error {
"delay",
"mode",
}
if err := helper.CheckHCLKeys(obj.Val, valid); err != nil {
if err := checkHCLKeys(obj.Val, valid); err != nil {
return err
}
@@ -308,7 +307,7 @@ func parseVolumes(out *map[string]*api.VolumeRequest, list *ast.ObjectList) erro
hcl.DecodeObject(out, list)
for k, v := range *out {
err := helper.UnusedKeys(v)
err := unusedKeys(v)
if err != nil {
return err
}
@@ -343,7 +342,7 @@ func parseScalingPolicy(out **api.ScalingPolicy, list *ast.ObjectList) error {
"policy",
"enabled",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -41,8 +40,8 @@ func parseJob(result *api.Job, list *ast.ObjectList) error {
delete(m, "multiregion")
// Set the ID and name to the object key
result.ID = helper.StringToPtr(obj.Keys[0].Token.Value().(string))
result.Name = helper.StringToPtr(*result.ID)
result.ID = stringToPtr(obj.Keys[0].Token.Value().(string))
result.Name = stringToPtr(*result.ID)
// Decode the rest
if err := mapstructure.WeakDecode(m, result); err != nil {
@@ -83,7 +82,7 @@ func parseJob(result *api.Job, list *ast.ObjectList) error {
"consul_token",
"multiregion",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, "job:")
}
@@ -176,7 +175,7 @@ func parseJob(result *api.Job, list *ast.ObjectList) error {
result.TaskGroups = make([]*api.TaskGroup, len(tasks), len(tasks)*2)
for i, t := range tasks {
result.TaskGroups[i] = &api.TaskGroup{
Name: helper.StringToPtr(t.Name),
Name: stringToPtr(t.Name),
Tasks: []*api.Task{t},
}
}
@@ -192,8 +191,8 @@ func parseJob(result *api.Job, list *ast.ObjectList) error {
// If we have a vault block, then parse that
if o := listVal.Filter("vault"); len(o.Items) > 0 {
jobVault := &api.Vault{
Env: helper.BoolToPtr(true),
ChangeMode: helper.StringToPtr("restart"),
Env: boolToPtr(true),
ChangeMode: stringToPtr("restart"),
}
if err := parseVault(jobVault, o); err != nil {
@@ -234,7 +233,7 @@ func parsePeriodic(result **api.PeriodicConfig, list *ast.ObjectList) error {
"prohibit_overlap",
"time_zone",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -281,7 +280,7 @@ func parseParameterizedJob(result **api.ParameterizedJobConfig, list *ast.Object
"meta_required",
"meta_optional",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -41,7 +40,7 @@ func parseMultiregion(result *api.Multiregion, list *ast.ObjectList) error {
"strategy",
"region",
}
if err := helper.CheckHCLKeys(obj.Val, valid); err != nil {
if err := checkHCLKeys(obj.Val, valid); err != nil {
return err
}
@@ -76,7 +75,7 @@ func parseMultiregionStrategy(final **api.MultiregionStrategy, list *ast.ObjectL
"max_parallel",
"on_failure",
}
if err := helper.CheckHCLKeys(obj.Val, valid); err != nil {
if err := checkHCLKeys(obj.Val, valid); err != nil {
return err
}
@@ -133,7 +132,7 @@ func parseMultiregionRegions(result *api.Multiregion, list *ast.ObjectList) erro
"datacenters",
"meta",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n))
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -25,7 +24,7 @@ func ParseNetwork(o *ast.ObjectList) (*api.NetworkResource, error) {
"dns",
"port",
}
if err := helper.CheckHCLKeys(o.Items[0].Val, valid); err != nil {
if err := checkHCLKeys(o.Items[0].Val, valid); err != nil {
return nil, multierror.Prefix(err, "network ->")
}
@@ -81,7 +80,7 @@ func parsePorts(networkObj *ast.ObjectList, nw *api.NetworkResource) error {
"to",
"host_network",
}
if err := helper.CheckHCLKeys(port.Val, valid); err != nil {
if err := checkHCLKeys(port.Val, valid); err != nil {
return err
}
@@ -119,7 +118,7 @@ func parseDNS(dns *ast.ObjectItem) (*api.DNSConfig, error) {
"options",
}
if err := helper.CheckHCLKeys(dns.Val, valid); err != nil {
if err := checkHCLKeys(dns.Val, valid); err != nil {
return nil, multierror.Prefix(err, "dns ->")
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -51,7 +50,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
"meta",
"canary_meta",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, err
}
@@ -151,7 +150,7 @@ func parseConnect(co *ast.ObjectItem) (*api.ConsulConnect, error) {
"sidecar_task",
}
if err := helper.CheckHCLKeys(co.Val, valid); err != nil {
if err := checkHCLKeys(co.Val, valid); err != nil {
return nil, multierror.Prefix(err, "connect ->")
}
@@ -227,7 +226,7 @@ func parseGateway(o *ast.ObjectItem) (*api.ConsulGateway, error) {
"ingress",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "gateway ->")
}
@@ -299,7 +298,7 @@ func parseGatewayProxy(o *ast.ObjectItem) (*api.ConsulGatewayProxy, error) {
"config",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "proxy ->")
}
@@ -385,7 +384,7 @@ func parseConsulIngressService(o *ast.ObjectItem) (*api.ConsulIngressService, er
"hosts",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "service ->")
}
@@ -416,7 +415,7 @@ func parseConsulIngressListener(o *ast.ObjectItem) (*api.ConsulIngressListener,
"service",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "listener ->")
}
@@ -467,7 +466,7 @@ func parseConsulGatewayTLS(o *ast.ObjectItem) (*api.ConsulGatewayTLSConfig, erro
"enabled",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "tls ->")
}
@@ -497,7 +496,7 @@ func parseIngressConfigEntry(o *ast.ObjectItem) (*api.ConsulIngressConfigEntry,
"listener",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "ingress ->")
}
@@ -551,7 +550,7 @@ func parseSidecarService(o *ast.ObjectItem) (*api.ConsulSidecarService, error) {
"tags",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "sidecar_service ->")
}
@@ -653,7 +652,7 @@ func parseProxy(o *ast.ObjectItem) (*api.ConsulProxy, error) {
"config",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return nil, multierror.Prefix(err, "proxy ->")
}
@@ -738,7 +737,7 @@ func parseExpose(eo *ast.ObjectItem) (*api.ConsulExposeConfig, error) {
"path", // an array of path blocks
}
if err := helper.CheckHCLKeys(eo.Val, valid); err != nil {
if err := checkHCLKeys(eo.Val, valid); err != nil {
return nil, multierror.Prefix(err, "expose ->")
}
@@ -776,7 +775,7 @@ func parseExposePath(epo *ast.ObjectItem) (*api.ConsulExposePath, error) {
"listener_port",
}
if err := helper.CheckHCLKeys(epo.Val, valid); err != nil {
if err := checkHCLKeys(epo.Val, valid); err != nil {
return nil, multierror.Prefix(err, "path ->")
}
@@ -806,7 +805,7 @@ func parseUpstream(uo *ast.ObjectItem) (*api.ConsulUpstream, error) {
"local_bind_port",
}
if err := helper.CheckHCLKeys(uo.Val, valid); err != nil {
if err := checkHCLKeys(uo.Val, valid); err != nil {
return nil, multierror.Prefix(err, "upstream ->")
}
@@ -859,7 +858,7 @@ func parseChecks(service *api.Service, checkObjs *ast.ObjectList) error {
"success_before_passing",
"failures_before_critical",
}
if err := helper.CheckHCLKeys(co.Val, valid); err != nil {
if err := checkHCLKeys(co.Val, valid); err != nil {
return multierror.Prefix(err, "check ->")
}
@@ -945,7 +944,7 @@ func parseCheckRestart(cro *ast.ObjectItem) (*api.CheckRestart, error) {
"ignore_warnings",
}
if err := helper.CheckHCLKeys(cro.Val, valid); err != nil {
if err := checkHCLKeys(cro.Val, valid); err != nil {
return nil, multierror.Prefix(err, "check_restart ->")
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/hashicorp/hcl"
"github.com/hashicorp/hcl/hcl/ast"
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/helper"
"github.com/mitchellh/mapstructure"
)
@@ -87,7 +86,7 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
}
// Check for invalid keys
if err := helper.CheckHCLKeys(listVal, keys); err != nil {
if err := checkHCLKeys(listVal, keys); err != nil {
return nil, err
}
@@ -247,7 +246,7 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
"max_files",
"max_file_size",
}
if err := helper.CheckHCLKeys(logsBlock.Val, valid); err != nil {
if err := checkHCLKeys(logsBlock.Val, valid); err != nil {
return nil, multierror.Prefix(err, "logs ->")
}
@@ -280,8 +279,8 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
// If we have a vault block, then parse that
if o := listVal.Filter("vault"); len(o.Items) > 0 {
v := &api.Vault{
Env: helper.BoolToPtr(true),
ChangeMode: helper.StringToPtr("restart"),
Env: boolToPtr(true),
ChangeMode: stringToPtr("restart"),
}
if err := parseVault(v, o); err != nil {
@@ -303,7 +302,7 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
valid := []string{
"file",
}
if err := helper.CheckHCLKeys(dispatchBlock.Val, valid); err != nil {
if err := checkHCLKeys(dispatchBlock.Val, valid); err != nil {
return nil, multierror.Prefix(err, "dispatch_payload ->")
}
@@ -331,7 +330,7 @@ func parseTask(item *ast.ObjectItem, keys []string) (*api.Task, error) {
"hook",
"sidecar",
}
if err := helper.CheckHCLKeys(lifecycleBlock.Val, valid); err != nil {
if err := checkHCLKeys(lifecycleBlock.Val, valid); err != nil {
return nil, multierror.Prefix(err, "lifecycle ->")
}
@@ -356,7 +355,7 @@ func parseArtifacts(result *[]*api.TaskArtifact, list *ast.ObjectList) error {
"mode",
"destination",
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -430,7 +429,7 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
"env",
"vault_grace", //COMPAT(0.12) not used; emits warning in 0.11.
}
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
if err := checkHCLKeys(o.Val, valid); err != nil {
return err
}
@@ -440,9 +439,9 @@ func parseTemplates(result *[]*api.Template, list *ast.ObjectList) error {
}
templ := &api.Template{
ChangeMode: helper.StringToPtr("restart"),
Splay: helper.TimeToPtr(5 * time.Second),
Perms: helper.StringToPtr("0644"),
ChangeMode: stringToPtr("restart"),
Splay: timeToPtr(5 * time.Second),
Perms: stringToPtr("0644"),
}
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
@@ -492,7 +491,7 @@ func parseResources(result *api.Resources, list *ast.ObjectList) error {
"network",
"device",
}
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
if err := checkHCLKeys(listVal, valid); err != nil {
return multierror.Prefix(err, "resources ->")
}
@@ -542,7 +541,7 @@ func parseResources(result *api.Resources, list *ast.ObjectList) error {
"affinity",
"constraint",
}
if err := helper.CheckHCLKeys(do.Val, valid); err != nil {
if err := checkHCLKeys(do.Val, valid); err != nil {
return multierror.Prefix(err, fmt.Sprintf("resources, device[%d]->", idx))
}
@@ -593,7 +592,7 @@ func parseVolumeMounts(out *[]*api.VolumeMount, list *ast.ObjectList) error {
"destination",
"propagation_mode",
}
if err := helper.CheckHCLKeys(item.Val, valid); err != nil {
if err := checkHCLKeys(item.Val, valid); err != nil {
return err
}

File diff suppressed because it is too large Load Diff