diff --git a/go/fn/cel.go b/go/fn/cel.go new file mode 100644 index 000000000..e90a597c7 --- /dev/null +++ b/go/fn/cel.go @@ -0,0 +1,199 @@ +// Copyright 2023 The kpt Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fn + +import ( + "fmt" + "reflect" + + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/common/types/traits" + // TODO: including this requires many dependency updates, at some point + // we should do that so the CEL evaluation here is consistent with + // K8s. There are a few other lines to uncomment in that case. + //"k8s.io/apiserver/pkg/cel/library" +) + +const ( + PkgContextVarName = "package" + UntypedItemsVarName = "items" +) + +func (ko *KubeObject) ToUntyped() (interface{}, error) { + return ko.obj.ToUntyped() +} + +func (rl *ResourceList) ResolveCEL(celExpr string) (string, error) { + inputs, err := rl.untypedCELInputs() + if err != nil { + return "", err + } + + return evalExpr(celExpr, inputs) +} + +func (rl *ResourceList) untypedCELInputs() (map[string]interface{}, error) { + inputs := make(map[string]interface{}) + var items []interface{} + + for _, ko := range rl.Items { + ut, err := ko.ToUntyped() + if err != nil { + return nil, err + } + items = append(items, ut) + } + + inputs[UntypedItemsVarName] = items + + return inputs, nil +} + +func evalExpr(expr string, inputs map[string]interface{}) (string, error) { + prog, err := compileExpr(expr) + if err != nil { + return "", err + } + + val, _, err := prog.Eval(inputs) + if err != nil { + return "", err + } + + result, err := val.ConvertToNative(reflect.TypeOf("")) + if err != nil { + return "", err + } + + s, ok := result.(string) + if !ok { + return "", fmt.Errorf("expression returned non-string value: %v", result) + } + + return s, nil +} + +func unaryWithFunction(name string, adapter types.Adapter, path ...string) cel.EnvOption { + fnname := fmt.Sprintf("with_%s", name) + id := fmt.Sprintf("resourcelist_with_%s_string", name) + return cel.Function(fnname, + cel.MemberOverload(id, []*cel.Type{cel.ListType(cel.DynType), cel.StringType}, cel.DynType, + cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val { + list := arg1.(traits.Lister) + result := types.NewDynamicList(adapter, []any{}) + + // loop through the list items to select the ones that match our criteria + i := list.Iterator() + for v := i.Next(); v != nil; v = i.Next() { + mapper, ok := v.(traits.Mapper) + if !ok { + // if the entry is not a mapper, just skip it + continue + } + // navigate through mappers for each field except the last + for _, field := range path[:len(path)-1] { + vv, ok := mapper.Find(adapter.NativeToValue(field)) + if !ok { + // no value found for the field name + // skip this list entry + mapper = nil + break + } + mapper, ok = vv.(traits.Mapper) + if !ok { + // value found for the field name is not a mapper + // skip this list entry + mapper = nil + break + } + } + if mapper == nil { + // we could not successfully navigate the path, skip this list entry + continue + } + // now pull the last field from the path; the result should be our + // value you we want to check against + testVal, ok := mapper.Find(adapter.NativeToValue(path[len(path)-1])) + if !ok { + // no such field, skip this list entry + continue + } + // found the test value, compare it to the argument + // and add it to the results list if found + if testVal.Equal(arg2) == types.True { + newResult := result.Add(types.NewDynamicList(adapter, []any{v})) + result, ok = newResult.(traits.Lister) + if !ok { + continue + } + } + } + if result.Size() == types.IntOne { + // if we found exactly one result, then return that result rather + // than a list of one entry, avoiding the need to use the [0] indexing + // notation + return result.Get(types.IntZero) + } + + // return the list result, so we can chain these + return result + }), + ), + ) +} + +// compileExpr returns a compiled CEL expression. +func compileExpr(expr string) (cel.Program, error) { + var opts []cel.EnvOption + opts = append(opts, cel.HomogeneousAggregateLiterals()) + opts = append(opts, cel.EagerlyValidateDeclarations(true), cel.DefaultUTCTimeZone(true)) + + // TODO: uncomment after updating to latest k8s + //opts = append(opts, library.ExtensionLibs...) + + opts = append(opts, cel.Variable(UntypedItemsVarName, cel.ListType(cel.DynType))) + + env, err := cel.NewEnv(opts...) + if err != nil { + return nil, err + } + + env, err = env.Extend( + unaryWithFunction("apiVersion", env.CELTypeAdapter(), "apiVersion"), + unaryWithFunction("kind", env.CELTypeAdapter(), "kind"), + unaryWithFunction("name", env.CELTypeAdapter(), "metadata", "name"), + unaryWithFunction("namespace", env.CELTypeAdapter(), "metadata", "namespace"), + ) + if err != nil { + return nil, err + } + + ast, issues := env.Compile(expr) + if issues != nil { + return nil, issues.Err() + } + + _, err = cel.AstToCheckedExpr(ast) + if err != nil { + return nil, err + } + return env.Program(ast, + cel.EvalOptions(cel.OptOptimize), + // TODO: uncomment after updating to latest k8s + //cel.OptimizeRegex(library.ExtensionLibRegexOptimizations...), + ) +} diff --git a/go/fn/cel_test.go b/go/fn/cel_test.go new file mode 100644 index 000000000..18d3ddec2 --- /dev/null +++ b/go/fn/cel_test.go @@ -0,0 +1,150 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fn + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResolveCEL(t *testing.T) { + rl, err := ParseResourceList([]byte(` +apiVersion: config.kubernetes.io/v1 +kind: ResourceList +items: +- apiVersion: v1 + kind: Namespace + metadata: + name: example +- apiVersion: v1 + kind: Namespace + metadata: + name: example2 + annotations: + foo: bar +- apiVersion: v1 + kind: ConfigMap + metadata: + name: example2 + namespace: ns-1 +- apiVersion: v1 + kind: ConfigMap + metadata: + name: example2 + namespace: ns-2 + data: + mykey: myvalue +- apiVersion: v1 + kind: ConfigMap + metadata: + name: example3 + namespace: ns-2 +- apiVersion: apps/v1 + kind: Deployment + metadata: + name: nginx-deployment + labels: + app: nginx + hey: there + annotations: + config.kubernetes.io/index: '0' + config.kubernetes.io/path: 'resources.yaml' + internal.config.kubernetes.io/index: '0' + internal.config.kubernetes.io/path: 'resources.yaml' + internal.config.kubernetes.io/seqindent: 'compact' + spec: + replicas: 3 + selector: + matchLabels: + app: nginx + paused: true + strategy: + type: Recreate + template: + metadata: + labels: + app: nginx + spec: + nodeSelector: + disktype: ssd + containers: + - name: nginx + image: nginx:1.14.2 + ports: + - containerPort: 80 + fakeStringSlice: + - test1 + - test2 +`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + testCases := map[string]struct { + expr string + expResult string + expError string + }{ + "string literal": { + expr: `"foo"`, + expResult: "foo", + }, + "int literal": { + expr: `15`, + expError: "unsupported type conversion from 'int' to string", + }, + "int literal conversion": { + expr: `string(15)`, + expResult: "15", + }, + "string var": { + expr: `items.filter(i, i.kind == "Deployment")[0].spec.strategy.type`, + expResult: "Recreate", + }, + "with_kind": { + expr: `items.with_kind("Deployment").spec.strategy.type`, + expResult: "Recreate", + }, + "with_name": { + expr: `items.with_name("nginx-deployment").spec.strategy.type`, + expResult: "Recreate", + }, + "with_namespace.with_name": { + expr: `items.with_namespace("ns-2").with_name("example2").data.mykey`, + expResult: "myvalue", + }, + "with_kind.with_name": { + expr: `items.with_kind("Namespace").with_name("example2").metadata.annotations["foo"]`, + expResult: "bar", + }, + "with_apiversion.with_name.with_kind": { + expr: `items.with_apiVersion("v1").with_name("example2").with_kind("Namespace").metadata.annotations["foo"]`, + expResult: "bar", + }, + } + + for tn, tc := range testCases { + t.Run(tn, func(t *testing.T) { + result, err := rl.ResolveCEL(tc.expr) + if tc.expError != "" { + assert.EqualError(t, err, tc.expError) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expResult, result) + } + }) + } +} diff --git a/go/fn/go.mod b/go/fn/go.mod index 71e55b3de..353080cfa 100644 --- a/go/fn/go.mod +++ b/go/fn/go.mod @@ -7,6 +7,7 @@ require ( github.com/go-errors/errors v1.0.1 github.com/google/go-cmp v0.5.9 github.com/stretchr/testify v1.8.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect k8s.io/apimachinery v0.24.0 // We must not include any core k8s APIs (e.g. k8s.io/api) in // the dependencies, depending on them will likely to cause version skew for @@ -16,16 +17,19 @@ require ( ) +require github.com/google/cel-go v0.18.0 + require ( github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/swag v0.21.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect github.com/josharian/intern v1.0.0 // indirect github.com/kr/pretty v0.3.0 // indirect @@ -34,10 +38,13 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/xlab/treeprint v1.1.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/text v0.7.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/protobuf v1.31.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go/fn/go.sum b/go/fn/go.sum index 951dded42..a0e379670 100644 --- a/go/fn/go.sum +++ b/go/fn/go.sum @@ -47,6 +47,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h1:7RFfzj4SSt6nnvCPbCqijJi1nWCd+TqAT3bYCStRC18= +github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -134,10 +136,13 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.18.0 h1:u74MPiEC8mejBrkXqrTWT102g5IFEUjxOngzQIijMzU= +github.com/google/cel-go v0.18.0/go.mod h1:PVAybmSnWkNMUZR/tEWFUiJ1Np4Hz0MHsZJcgC4zln4= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -288,6 +293,7 @@ github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t6 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -342,6 +348,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= +golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -408,8 +416,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -494,8 +502,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -627,6 +635,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= +google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 h1:eSaPbMR4T7WfH9FvABk36NBMacoTUKdWCvV0dx+KfOg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -660,8 +672,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go/fn/internal/map.go b/go/fn/internal/map.go index 4da8ecf41..64e1d5bb9 100644 --- a/go/fn/internal/map.go +++ b/go/fn/internal/map.go @@ -310,3 +310,21 @@ func (o *MapVariant) GetMap(field string) *MapVariant { return nil } + +func (o *MapVariant) ToUntyped() (interface{}, error) { + entries, err := o.Entries() + if err != nil { + return nil, err + } + + result := make(map[string]interface{}) + for k, v := range entries { + vv, err := v.ToUntyped() + if err != nil { + return nil, fmt.Errorf("k: %v, v: %v, err: %w", k, v, err) + } + result[k] = vv + } + + return result, nil +} diff --git a/go/fn/internal/scalar.go b/go/fn/internal/scalar.go index 5aa15c629..61c9c8964 100644 --- a/go/fn/internal/scalar.go +++ b/go/fn/internal/scalar.go @@ -115,3 +115,7 @@ func (v *scalarVariant) FloatValue() (float64, bool) { func (v *scalarVariant) Node() *yaml.Node { return v.node } + +func (v *scalarVariant) ToUntyped() (interface{}, error) { + return v.node.Value, nil +} diff --git a/go/fn/internal/slice.go b/go/fn/internal/slice.go index 1c2f64f20..9a91e0f41 100644 --- a/go/fn/internal/slice.go +++ b/go/fn/internal/slice.go @@ -49,3 +49,16 @@ func (v *sliceVariant) Elements() ([]*MapVariant, error) { func (v *sliceVariant) Add(node variant) { v.node.Content = append(v.node.Content, node.Node()) } + +func (v *sliceVariant) ToUntyped() (interface{}, error) { + var result []interface{} + for _, n := range v.node.Content { + vv, err := toVariant(n).ToUntyped() + if err != nil { + return nil, err + } + result = append(result, vv) + } + + return result, nil +} diff --git a/go/fn/internal/variant.go b/go/fn/internal/variant.go index 0d993a47d..7f9065750 100644 --- a/go/fn/internal/variant.go +++ b/go/fn/internal/variant.go @@ -34,6 +34,7 @@ const ( type variant interface { GetKind() variantKind Node() *yaml.Node + ToUntyped() (interface{}, error) } // nodes are expected to be key1,value1,key2,value2,...