From 7221939fe949a2dbcefe39510dcbb024ce2cacc4 Mon Sep 17 00:00:00 2001 From: longkai Date: Sun, 26 Feb 2023 09:54:47 +0000 Subject: [PATCH] feat(engine): add jq filter template func resolve helm#11212 Signed-off-by: longkai --- go.mod | 5 ++++- go.sum | 6 ++++++ pkg/engine/funcs.go | 40 +++++++++++++++++++++++++++++++++++++--- pkg/engine/funcs_test.go | 22 ++++++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index b3b762745..f280104a9 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,8 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +require github.com/itchyny/timefmt-go v0.1.5 // indirect + require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect @@ -96,6 +98,7 @@ require ( github.com/huandu/xstrings v1.4.0 // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/itchyny/gojq v0.12.11 github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.11.13 // indirect @@ -105,7 +108,7 @@ require ( github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index 14e11b200..39f1074c3 100644 --- a/go.sum +++ b/go.sum @@ -701,6 +701,10 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= +github.com/itchyny/gojq v0.12.11 h1:YhLueoHhHiN4mkfM+3AyJV6EPcCxKZsOnYf+aVSwaQw= +github.com/itchyny/gojq v0.12.11/go.mod h1:o3FT8Gkbg/geT4pLI0tF3hvip5F3Y/uskjRz9OYa38g= +github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= +github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -798,6 +802,7 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= @@ -1017,6 +1022,7 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index 92b4c3383..5ad2f273b 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -24,6 +24,7 @@ import ( "github.com/BurntSushi/toml" "github.com/Masterminds/sprig/v3" + "github.com/itchyny/gojq" "sigs.k8s.io/yaml" ) @@ -35,12 +36,11 @@ import ( // // Known late-bound functions: // -// - "include" -// - "tpl" +// - "include" +// - "tpl" // // These are late-bound in Engine.Render(). The // version included in the FuncMap is a placeholder. -// func funcMap() template.FuncMap { f := sprig.TxtFuncMap() delete(f, "env") @@ -55,6 +55,7 @@ func funcMap() template.FuncMap { "toJson": toJSON, "fromJson": fromJSON, "fromJsonArray": fromJSONArray, + "jq": jq, // This is a placeholder for the "include" function, which is // late-bound to a template. By declaring it here, we preserve the @@ -175,3 +176,36 @@ func fromJSONArray(str string) []interface{} { } return a } + +// jq is like sed for JSON/YAML data - you can use it to slice and filter +// and map and transform structured data with the same ease that sed, awk, grep +// and friends let you play with text. +// +// See the [jq Tutorial](https://stedolan.github.io/jq/tutorial/) for more. +// +// This is a pure go implementation of jq. The type of v should be +// []interface{} for an array or map[string]interface{} for a map. +// Since the jq filter returns a iterator, it will be collected as a slice +// even there is only one item. +// It will insert the returned error message string as +// the first and only item in the returned array. +func jq(exp string, v interface{}) []interface{} { + query, err := gojq.Parse(exp) + if err != nil { + return []interface{}{err.Error()} + } + + iter := query.Run(v) + var list []interface{} + for { + it, ok := iter.Next() + if !ok { + break + } + if err, ok := it.(error); ok { + return []interface{}{err.Error()} + } + list = append(list, it) + } + return list +} diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index 29bc121b5..2845de5ba 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -99,6 +99,28 @@ func TestFuncs(t *testing.T) { tpl: `{{ lookup "v1" "Namespace" "" "unlikelynamespace99999999" }}`, expect: `map[]`, vars: `["one", "two"]`, + }, { + tpl: "{{ . | jq `.ports[] | select(.name == \"grpc\") | .containerPort` | first }}", + expect: "8080", + vars: map[string]interface{}{ + "ports": []interface{}{ + map[string]interface{}{ + "containerPort": 80, + "name": "http", + "protocol": "TCP", + }, + map[string]interface{}{ + "containerPort": 443, + "name": "https", + "protocol": "TCP", + }, + map[string]interface{}{ + "containerPort": 8080, + "name": "grpc", + "protocol": "TCP", + }, + }, + }, }} for _, tt := range tests {