lang: types, unification: Don't recurse into private fields
We forgot to omit looking deeper into private struct fields. I don't know why we didn't catch this earlier, I can only assume some subtlety changed, since we've previously used many of the resources this would fail on. Maybe golang broke some API that they didn't consider stable? This also adds a new test for this, and ensures each resource can be inspected too!
This commit is contained in:
@@ -120,6 +120,7 @@ func TypeOf(t reflect.Type) (*Type, error) {
|
||||
StructTagOpt(StructTag),
|
||||
StrictStructTagOpt(false),
|
||||
SkipBadStructFieldsOpt(false),
|
||||
SkipPrivateFieldsOpt(false),
|
||||
AllowInterfaceTypeOpt(false),
|
||||
}
|
||||
return ConfigurableTypeOf(t, opts...)
|
||||
@@ -132,6 +133,7 @@ func ResTypeOf(t reflect.Type) (*Type, error) {
|
||||
StructTagOpt(StructTag),
|
||||
StrictStructTagOpt(true),
|
||||
SkipBadStructFieldsOpt(true),
|
||||
SkipPrivateFieldsOpt(true),
|
||||
AllowInterfaceTypeOpt(true),
|
||||
}
|
||||
return ConfigurableTypeOf(t, opts...)
|
||||
@@ -146,6 +148,7 @@ type typeOfOptions struct {
|
||||
structTag string
|
||||
strictStructTag bool
|
||||
skipBadStructFields bool
|
||||
skipPrivateFields bool
|
||||
allowInterfaceType bool
|
||||
// TODO: add more options
|
||||
}
|
||||
@@ -175,6 +178,14 @@ func SkipBadStructFieldsOpt(skipBadStructFields bool) TypeOfOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipPrivateFieldsOpt specifies whether we should skip over struct fields that
|
||||
// are private or unexported. This is used by ResTypeOf.
|
||||
func SkipPrivateFieldsOpt(skipPrivateFields bool) TypeOfOption {
|
||||
return func(opt *typeOfOptions) {
|
||||
opt.skipPrivateFields = skipPrivateFields
|
||||
}
|
||||
}
|
||||
|
||||
// AllowInterfaceTypeOpt specifies whether we should allow matching on an
|
||||
// interface kind. This is used by ResTypeOf.
|
||||
func AllowInterfaceTypeOpt(allowInterfaceType bool) TypeOfOption {
|
||||
@@ -190,6 +201,7 @@ func ConfigurableTypeOf(t reflect.Type, opts ...TypeOfOption) (*Type, error) {
|
||||
structTag: "",
|
||||
strictStructTag: false,
|
||||
skipBadStructFields: false,
|
||||
skipPrivateFields: false,
|
||||
allowInterfaceType: false,
|
||||
}
|
||||
for _, optionFunc := range opts { // apply the options
|
||||
@@ -271,6 +283,9 @@ func ConfigurableTypeOf(t reflect.Type, opts ...TypeOfOption) (*Type, error) {
|
||||
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
if options.skipPrivateFields && !field.IsExported() { // prevent infinite recursion
|
||||
continue
|
||||
}
|
||||
tt, err := ConfigurableTypeOf(field.Type, opts...)
|
||||
if err != nil {
|
||||
if options.skipBadStructFields {
|
||||
|
||||
@@ -34,10 +34,13 @@ package solvers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/purpleidea/mgmt/engine"
|
||||
_ "github.com/purpleidea/mgmt/engine/resources" // import so the resources register
|
||||
engineUtil "github.com/purpleidea/mgmt/engine/util"
|
||||
"github.com/purpleidea/mgmt/lang/ast"
|
||||
"github.com/purpleidea/mgmt/lang/funcs/operators"
|
||||
"github.com/purpleidea/mgmt/lang/funcs/vars"
|
||||
@@ -982,3 +985,41 @@ func TestUnification1(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLangFieldNameToStructType1(t *testing.T) {
|
||||
for _, kind := range engine.RegisteredResourcesNames() {
|
||||
t.Logf("res.Kind(): %s", kind)
|
||||
|
||||
res, err := engineUtil.LangFieldNameToStructType(kind)
|
||||
if err != nil {
|
||||
t.Errorf("error trying to inspect kind %s: %s", kind, err.Error())
|
||||
continue
|
||||
}
|
||||
if res == nil {
|
||||
t.Errorf("got nil when trying to inspect kind: %s", kind)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLangFieldNameToStructType2(t *testing.T) {
|
||||
// This tests that we don't infinitely recurse inside of this function.
|
||||
k := "consul:kv"
|
||||
res, err := engineUtil.LangFieldNameToStructType(k)
|
||||
|
||||
expected := map[string]*types.Type{
|
||||
"address": types.TypeStr,
|
||||
"key": types.TypeStr,
|
||||
"scheme": types.TypeStr,
|
||||
"token": types.TypeStr,
|
||||
"value": types.TypeStr,
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("error trying to get the field name type map: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
t.Errorf("unexpected result: %+v", res)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user