support fieldselector filter query secrets (#5300)

* support fieldselector filter query secrets

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>

* support fieldselector filter query secrets

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>

* support fieldselector filter query secrets

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>

* support fieldselector filter query secrets

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>

* support fieldselector filter query secrets

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>

Signed-off-by: wuzhongjian <wuzhongjian_yewu@cmss.chinamobile.com>
This commit is contained in:
nuclearwu
2022-10-26 10:14:49 +08:00
committed by GitHub
parent 1e79901241
commit 89b1b6db87
10 changed files with 956 additions and 0 deletions

1
go.mod
View File

@@ -217,6 +217,7 @@ require (
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/nxadm/tail v1.4.4 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/opencontainers/runc v0.1.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect

2
go.sum
View File

@@ -638,6 +638,8 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=

View File

@@ -88,6 +88,7 @@ func AddToContainer(c *restful.Container, informerFactory informers.InformerFact
Param(webservice.QueryParameter(query.ParameterLimit, "limit").Required(false)).
Param(webservice.QueryParameter(query.ParameterAscending, "sort parameters, e.g. reverse=true").Required(false).DefaultValue("ascending=false")).
Param(webservice.QueryParameter(query.ParameterOrderBy, "sort parameters, e.g. orderBy=createTime")).
Param(webservice.QueryParameter(query.ParameterFieldSelector, "field selector used for filtering, you can use the = , == and != operators with field selectors( = and == mean the same thing), e.g. fieldSelector=type=kubernetes.io/dockerconfigjson, multiple separated by comma").Required(false)).
Returns(http.StatusOK, ok, api.ListResult{}))
webservice.Route(webservice.GET("/namespaces/{namespace}/{resources}/{name}").

View File

@@ -17,13 +17,20 @@ limitations under the License.
package secret
import (
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/client-go/informers"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"github.com/oliveagle/jsonpath"
v1 "k8s.io/api/core/v1"
)
@@ -74,5 +81,55 @@ func (s *secretSearcher) filter(object runtime.Object, filter query.Filter) bool
return false
}
if filter.Field == query.ParameterFieldSelector {
return contains(secret, filter.Value)
}
return v1alpha3.DefaultObjectMetaFilter(secret.ObjectMeta, filter)
}
// implement a generic query filter to support multiple field selectors with "jsonpath.JsonPathLookup"
// https://github.com/oliveagle/jsonpath/blob/master/readme.md
func contains(secret *v1.Secret, queryValue query.Value) bool {
// call the ParseSelector function of "k8s.io/apimachinery/pkg/fields/selector.go" to validate and parse the selector
fieldSelector, err := fields.ParseSelector(string(queryValue))
if err != nil {
klog.V(4).Infof("failed parse selector error: %s", err)
return false
}
for _, requirement := range fieldSelector.Requirements() {
var negative bool
// supports '=', '==' and '!='.(e.g. ?fieldSelector=key1=value1,key2=value2)
switch requirement.Operator {
case selection.NotEquals:
negative = true
case selection.DoubleEquals:
case selection.Equals:
negative = false
}
key := requirement.Field
value := requirement.Value
var input map[string]interface{}
data, err := json.Marshal(secret)
if err != nil {
klog.V(4).Infof("failed marshal to JSON string: %s", err)
return false
}
if err = json.Unmarshal(data, &input); err != nil {
klog.V(4).Infof("failed unmarshal to map object: %s", err)
return false
}
rawValue, err := jsonpath.JsonPathLookup(input, "$."+key)
if err != nil {
klog.V(4).Infof("failed to lookup jsonpath: %s", err)
return false
}
if (negative && fmt.Sprintf("%v", rawValue) != value) || (!negative && fmt.Sprintf("%v", rawValue) == value) {
continue
} else {
return false
}
}
return true
}

26
vendor/github.com/oliveagle/jsonpath/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,26 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.sw[op]
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
.idea

8
vendor/github.com/oliveagle/jsonpath/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,8 @@
language: go
go:
- 1.5
- 1.5.1
- 1.6.2
os: linux

22
vendor/github.com/oliveagle/jsonpath/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 oliver
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

722
vendor/github.com/oliveagle/jsonpath/jsonpath.go generated vendored Normal file
View File

@@ -0,0 +1,722 @@
package jsonpath
import (
"errors"
"fmt"
"go/token"
"go/types"
"reflect"
"regexp"
"strconv"
"strings"
)
var ErrGetFromNullObj = errors.New("get attribute from null object")
func JsonPathLookup(obj interface{}, jpath string) (interface{}, error) {
c, err := Compile(jpath)
if err != nil {
return nil, err
}
return c.Lookup(obj)
}
type Compiled struct {
path string
steps []step
}
type step struct {
op string
key string
args interface{}
}
func MustCompile(jpath string) *Compiled {
c, err := Compile(jpath)
if err != nil {
panic(err)
}
return c
}
func Compile(jpath string) (*Compiled, error) {
tokens, err := tokenize(jpath)
if err != nil {
return nil, err
}
if tokens[0] != "@" && tokens[0] != "$" {
return nil, fmt.Errorf("$ or @ should in front of path")
}
tokens = tokens[1:]
res := Compiled{
path: jpath,
steps: make([]step, len(tokens)),
}
for i, token := range tokens {
op, key, args, err := parse_token(token)
if err != nil {
return nil, err
}
res.steps[i] = step{op, key, args}
}
return &res, nil
}
func (c *Compiled) String() string {
return fmt.Sprintf("Compiled lookup: %s", c.path)
}
func (c *Compiled) Lookup(obj interface{}) (interface{}, error) {
var err error
for _, s := range c.steps {
// "key", "idx"
switch s.op {
case "key":
obj, err = get_key(obj, s.key)
if err != nil {
return nil, err
}
case "idx":
if len(s.key) > 0 {
// no key `$[0].test`
obj, err = get_key(obj, s.key)
if err != nil {
return nil, err
}
}
if len(s.args.([]int)) > 1 {
res := []interface{}{}
for _, x := range s.args.([]int) {
//fmt.Println("idx ---- ", x)
tmp, err := get_idx(obj, x)
if err != nil {
return nil, err
}
res = append(res, tmp)
}
obj = res
} else if len(s.args.([]int)) == 1 {
//fmt.Println("idx ----------------3")
obj, err = get_idx(obj, s.args.([]int)[0])
if err != nil {
return nil, err
}
} else {
//fmt.Println("idx ----------------4")
return nil, fmt.Errorf("cannot index on empty slice")
}
case "range":
if len(s.key) > 0 {
// no key `$[:1].test`
obj, err = get_key(obj, s.key)
if err != nil {
return nil, err
}
}
if argsv, ok := s.args.([2]interface{}); ok == true {
obj, err = get_range(obj, argsv[0], argsv[1])
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("range args length should be 2")
}
case "filter":
obj, err = get_key(obj, s.key)
if err != nil {
return nil, err
}
obj, err = get_filtered(obj, obj, s.args.(string))
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("expression don't support in filter")
}
}
return obj, nil
}
func tokenize(query string) ([]string, error) {
tokens := []string{}
// token_start := false
// token_end := false
token := ""
// fmt.Println("-------------------------------------------------- start")
for idx, x := range query {
token += string(x)
// //fmt.Printf("idx: %d, x: %s, token: %s, tokens: %v\n", idx, string(x), token, tokens)
if idx == 0 {
if token == "$" || token == "@" {
tokens = append(tokens, token[:])
token = ""
continue
} else {
return nil, fmt.Errorf("should start with '$'")
}
}
if token == "." {
continue
} else if token == ".." {
if tokens[len(tokens)-1] != "*" {
tokens = append(tokens, "*")
}
token = "."
continue
} else {
// fmt.Println("else: ", string(x), token)
if strings.Contains(token, "[") {
// fmt.Println(" contains [ ")
if x == ']' && !strings.HasSuffix(token, "\\]") {
if token[0] == '.' {
tokens = append(tokens, token[1:])
} else {
tokens = append(tokens, token[:])
}
token = ""
continue
}
} else {
// fmt.Println(" doesn't contains [ ")
if x == '.' {
if token[0] == '.' {
tokens = append(tokens, token[1:len(token)-1])
} else {
tokens = append(tokens, token[:len(token)-1])
}
token = "."
continue
}
}
}
}
if len(token) > 0 {
if token[0] == '.' {
token = token[1:]
if token != "*" {
tokens = append(tokens, token[:])
} else if tokens[len(tokens)-1] != "*" {
tokens = append(tokens, token[:])
}
} else {
if token != "*" {
tokens = append(tokens, token[:])
} else if tokens[len(tokens)-1] != "*" {
tokens = append(tokens, token[:])
}
}
}
// fmt.Println("finished tokens: ", tokens)
// fmt.Println("================================================= done ")
return tokens, nil
}
/*
op: "root", "key", "idx", "range", "filter", "scan"
*/
func parse_token(token string) (op string, key string, args interface{}, err error) {
if token == "$" {
return "root", "$", nil, nil
}
if token == "*" {
return "scan", "*", nil, nil
}
bracket_idx := strings.Index(token, "[")
if bracket_idx < 0 {
return "key", token, nil, nil
} else {
key = token[:bracket_idx]
tail := token[bracket_idx:]
if len(tail) < 3 {
err = fmt.Errorf("len(tail) should >=3, %v", tail)
return
}
tail = tail[1 : len(tail)-1]
//fmt.Println(key, tail)
if strings.Contains(tail, "?") {
// filter -------------------------------------------------
op = "filter"
if strings.HasPrefix(tail, "?(") && strings.HasSuffix(tail, ")") {
args = strings.Trim(tail[2:len(tail)-1], " ")
}
return
} else if strings.Contains(tail, ":") {
// range ----------------------------------------------
op = "range"
tails := strings.Split(tail, ":")
if len(tails) != 2 {
err = fmt.Errorf("only support one range(from, to): %v", tails)
return
}
var frm interface{}
var to interface{}
if frm, err = strconv.Atoi(strings.Trim(tails[0], " ")); err != nil {
if strings.Trim(tails[0], " ") == "" {
err = nil
}
frm = nil
}
if to, err = strconv.Atoi(strings.Trim(tails[1], " ")); err != nil {
if strings.Trim(tails[1], " ") == "" {
err = nil
}
to = nil
}
args = [2]interface{}{frm, to}
return
} else if tail == "*" {
op = "range"
args = [2]interface{}{nil, nil}
return
} else {
// idx ------------------------------------------------
op = "idx"
res := []int{}
for _, x := range strings.Split(tail, ",") {
if i, err := strconv.Atoi(strings.Trim(x, " ")); err == nil {
res = append(res, i)
} else {
return "", "", nil, err
}
}
args = res
}
}
return op, key, args, nil
}
func filter_get_from_explicit_path(obj interface{}, path string) (interface{}, error) {
steps, err := tokenize(path)
//fmt.Println("f: steps: ", steps, err)
//fmt.Println(path, steps)
if err != nil {
return nil, err
}
if steps[0] != "@" && steps[0] != "$" {
return nil, fmt.Errorf("$ or @ should in front of path")
}
steps = steps[1:]
xobj := obj
//fmt.Println("f: xobj", xobj)
for _, s := range steps {
op, key, args, err := parse_token(s)
// "key", "idx"
switch op {
case "key":
xobj, err = get_key(xobj, key)
if err != nil {
return nil, err
}
case "idx":
if len(args.([]int)) != 1 {
return nil, fmt.Errorf("don't support multiple index in filter")
}
xobj, err = get_key(xobj, key)
if err != nil {
return nil, err
}
xobj, err = get_idx(xobj, args.([]int)[0])
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("expression don't support in filter")
}
}
return xobj, nil
}
func get_key(obj interface{}, key string) (interface{}, error) {
if reflect.TypeOf(obj) == nil {
return nil, ErrGetFromNullObj
}
switch reflect.TypeOf(obj).Kind() {
case reflect.Map:
// if obj came from stdlib json, its highly likely to be a map[string]interface{}
// in which case we can save having to iterate the map keys to work out if the
// key exists
if jsonMap, ok := obj.(map[string]interface{}); ok {
val, exists := jsonMap[key]
if !exists {
return nil, fmt.Errorf("key error: %s not found in object", key)
}
return val, nil
}
for _, kv := range reflect.ValueOf(obj).MapKeys() {
//fmt.Println(kv.String())
if kv.String() == key {
return reflect.ValueOf(obj).MapIndex(kv).Interface(), nil
}
}
return nil, fmt.Errorf("key error: %s not found in object", key)
case reflect.Slice:
// slice we should get from all objects in it.
res := []interface{}{}
for i := 0; i < reflect.ValueOf(obj).Len(); i++ {
tmp, _ := get_idx(obj, i)
if v, err := get_key(tmp, key); err == nil {
res = append(res, v)
}
}
return res, nil
default:
return nil, fmt.Errorf("object is not map")
}
}
func get_idx(obj interface{}, idx int) (interface{}, error) {
switch reflect.TypeOf(obj).Kind() {
case reflect.Slice:
length := reflect.ValueOf(obj).Len()
if idx >= 0 {
if idx >= length {
return nil, fmt.Errorf("index out of range: len: %v, idx: %v", length, idx)
}
return reflect.ValueOf(obj).Index(idx).Interface(), nil
} else {
// < 0
_idx := length + idx
if _idx < 0 {
return nil, fmt.Errorf("index out of range: len: %v, idx: %v", length, idx)
}
return reflect.ValueOf(obj).Index(_idx).Interface(), nil
}
default:
return nil, fmt.Errorf("object is not Slice")
}
}
func get_range(obj, frm, to interface{}) (interface{}, error) {
switch reflect.TypeOf(obj).Kind() {
case reflect.Slice:
length := reflect.ValueOf(obj).Len()
_frm := 0
_to := length
if frm == nil {
frm = 0
}
if to == nil {
to = length - 1
}
if fv, ok := frm.(int); ok == true {
if fv < 0 {
_frm = length + fv
} else {
_frm = fv
}
}
if tv, ok := to.(int); ok == true {
if tv < 0 {
_to = length + tv + 1
} else {
_to = tv + 1
}
}
if _frm < 0 || _frm >= length {
return nil, fmt.Errorf("index [from] out of range: len: %v, from: %v", length, frm)
}
if _to < 0 || _to > length {
return nil, fmt.Errorf("index [to] out of range: len: %v, to: %v", length, to)
}
//fmt.Println("_frm, _to: ", _frm, _to)
res_v := reflect.ValueOf(obj).Slice(_frm, _to)
return res_v.Interface(), nil
default:
return nil, fmt.Errorf("object is not Slice")
}
}
func regFilterCompile(rule string) (*regexp.Regexp, error) {
runes := []rune(rule)
if len(runes) <= 2 {
return nil, errors.New("empty rule")
}
if runes[0] != '/' || runes[len(runes)-1] != '/' {
return nil, errors.New("invalid syntax. should be in `/pattern/` form")
}
runes = runes[1 : len(runes)-1]
return regexp.Compile(string(runes))
}
func get_filtered(obj, root interface{}, filter string) ([]interface{}, error) {
lp, op, rp, err := parse_filter(filter)
if err != nil {
return nil, err
}
res := []interface{}{}
switch reflect.TypeOf(obj).Kind() {
case reflect.Slice:
if op == "=~" {
// regexp
pat, err := regFilterCompile(rp)
if err != nil {
return nil, err
}
for i := 0; i < reflect.ValueOf(obj).Len(); i++ {
tmp := reflect.ValueOf(obj).Index(i).Interface()
ok, err := eval_reg_filter(tmp, root, lp, pat)
if err != nil {
return nil, err
}
if ok == true {
res = append(res, tmp)
}
}
} else {
for i := 0; i < reflect.ValueOf(obj).Len(); i++ {
tmp := reflect.ValueOf(obj).Index(i).Interface()
ok, err := eval_filter(tmp, root, lp, op, rp)
if err != nil {
return nil, err
}
if ok == true {
res = append(res, tmp)
}
}
}
return res, nil
case reflect.Map:
if op == "=~" {
// regexp
pat, err := regFilterCompile(rp)
if err != nil {
return nil, err
}
for _, kv := range reflect.ValueOf(obj).MapKeys() {
tmp := reflect.ValueOf(obj).MapIndex(kv).Interface()
ok, err := eval_reg_filter(tmp, root, lp, pat)
if err != nil {
return nil, err
}
if ok == true {
res = append(res, tmp)
}
}
} else {
for _, kv := range reflect.ValueOf(obj).MapKeys() {
tmp := reflect.ValueOf(obj).MapIndex(kv).Interface()
ok, err := eval_filter(tmp, root, lp, op, rp)
if err != nil {
return nil, err
}
if ok == true {
res = append(res, tmp)
}
}
}
default:
return nil, fmt.Errorf("don't support filter on this type: %v", reflect.TypeOf(obj).Kind())
}
return res, nil
}
// @.isbn => @.isbn, exists, nil
// @.price < 10 => @.price, <, 10
// @.price <= $.expensive => @.price, <=, $.expensive
// @.author =~ /.*REES/i => @.author, match, /.*REES/i
func parse_filter(filter string) (lp string, op string, rp string, err error) {
tmp := ""
stage := 0
str_embrace := false
for idx, c := range filter {
switch c {
case '\'':
if str_embrace == false {
str_embrace = true
} else {
switch stage {
case 0:
lp = tmp
case 1:
op = tmp
case 2:
rp = tmp
}
tmp = ""
}
case ' ':
if str_embrace == true {
tmp += string(c)
continue
}
switch stage {
case 0:
lp = tmp
case 1:
op = tmp
case 2:
rp = tmp
}
tmp = ""
stage += 1
if stage > 2 {
return "", "", "", errors.New(fmt.Sprintf("invalid char at %d: `%c`", idx, c))
}
default:
tmp += string(c)
}
}
if tmp != "" {
switch stage {
case 0:
lp = tmp
op = "exists"
case 1:
op = tmp
case 2:
rp = tmp
}
tmp = ""
}
return lp, op, rp, err
}
func parse_filter_v1(filter string) (lp string, op string, rp string, err error) {
tmp := ""
istoken := false
for _, c := range filter {
if istoken == false && c != ' ' {
istoken = true
}
if istoken == true && c == ' ' {
istoken = false
}
if istoken == true {
tmp += string(c)
}
if istoken == false && tmp != "" {
if lp == "" {
lp = tmp[:]
tmp = ""
} else if op == "" {
op = tmp[:]
tmp = ""
} else if rp == "" {
rp = tmp[:]
tmp = ""
}
}
}
if tmp != "" && lp == "" && op == "" && rp == "" {
lp = tmp[:]
op = "exists"
rp = ""
err = nil
return
} else if tmp != "" && rp == "" {
rp = tmp[:]
tmp = ""
}
return lp, op, rp, err
}
func eval_reg_filter(obj, root interface{}, lp string, pat *regexp.Regexp) (res bool, err error) {
if pat == nil {
return false, errors.New("nil pat")
}
lp_v, err := get_lp_v(obj, root, lp)
if err != nil {
return false, err
}
switch v := lp_v.(type) {
case string:
return pat.MatchString(v), nil
default:
return false, errors.New("only string can match with regular expression")
}
}
func get_lp_v(obj, root interface{}, lp string) (interface{}, error) {
var lp_v interface{}
if strings.HasPrefix(lp, "@.") {
return filter_get_from_explicit_path(obj, lp)
} else if strings.HasPrefix(lp, "$.") {
return filter_get_from_explicit_path(root, lp)
} else {
lp_v = lp
}
return lp_v, nil
}
func eval_filter(obj, root interface{}, lp, op, rp string) (res bool, err error) {
lp_v, err := get_lp_v(obj, root, lp)
if op == "exists" {
return lp_v != nil, nil
} else if op == "=~" {
return false, fmt.Errorf("not implemented yet")
} else {
var rp_v interface{}
if strings.HasPrefix(rp, "@.") {
rp_v, err = filter_get_from_explicit_path(obj, rp)
} else if strings.HasPrefix(rp, "$.") {
rp_v, err = filter_get_from_explicit_path(root, rp)
} else {
rp_v = rp
}
//fmt.Printf("lp_v: %v, rp_v: %v\n", lp_v, rp_v)
return cmp_any(lp_v, rp_v, op)
}
}
func isNumber(o interface{}) bool {
switch v := o.(type) {
case int, int8, int16, int32, int64:
return true
case uint, uint8, uint16, uint32, uint64:
return true
case float32, float64:
return true
case string:
_, err := strconv.ParseFloat(v, 64)
if err == nil {
return true
} else {
return false
}
}
return false
}
func cmp_any(obj1, obj2 interface{}, op string) (bool, error) {
switch op {
case "<", "<=", "==", ">=", ">":
default:
return false, fmt.Errorf("op should only be <, <=, ==, >= and >")
}
var exp string
if isNumber(obj1) && isNumber(obj2) {
exp = fmt.Sprintf(`%v %s %v`, obj1, op, obj2)
} else {
exp = fmt.Sprintf(`"%v" %s "%v"`, obj1, op, obj2)
}
//fmt.Println("exp: ", exp)
fset := token.NewFileSet()
res, err := types.Eval(fset, nil, 0, exp)
if err != nil {
return false, err
}
if res.IsValue() == false || (res.Value.String() != "false" && res.Value.String() != "true") {
return false, fmt.Errorf("result should only be true or false")
}
if res.Value.String() == "true" {
return true, nil
}
return false, nil
}

114
vendor/github.com/oliveagle/jsonpath/readme.md generated vendored Normal file
View File

@@ -0,0 +1,114 @@
JsonPath
----------------
![Build Status](https://travis-ci.org/oliveagle/jsonpath.svg?branch=master)
A golang implementation of JsonPath syntax.
follow the majority rules in http://goessner.net/articles/JsonPath/
but also with some minor differences.
this library is till bleeding edge, so use it at your own risk. :D
**Golang Version Required**: 1.5+
Get Started
------------
```bash
go get github.com/oliveagle/jsonpath
```
example code:
```go
import (
"github.com/oliveagle/jsonpath"
"encoding/json"
)
var json_data interface{}
json.Unmarshal([]byte(data), &json_data)
res, err := jsonpath.JsonPathLookup(json_data, "$.expensive")
//or reuse lookup pattern
pat, _ := jsonpath.Compile(`$.store.book[?(@.price < $.expensive)].price`)
res, err := pat.Lookup(json_data)
```
Operators
--------
referenced from github.com/jayway/JsonPath
| Operator | Supported | Description |
| ---- | :---: | ---------- |
| $ | Y | The root element to query. This starts all path expressions. |
| @ | Y | The current node being processed by a filter predicate. |
| * | X | Wildcard. Available anywhere a name or numeric are required. |
| .. | X | Deep scan. Available anywhere a name is required. |
| .<name> | Y | Dot-notated child |
| ['<name>' (, '<name>')] | X | Bracket-notated child or children |
| [<number> (, <number>)] | Y | Array index or indexes |
| [start:end] | Y | Array slice operator |
| [?(<expression>)] | Y | Filter expression. Expression must evaluate to a boolean value. |
Examples
--------
given these example data.
```javascript
{
"store": {
"book": [
{
"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{
"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{
"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{
"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
},
"expensive": 10
}
```
example json path syntax.
----
| jsonpath | result|
| :--------- | :-------|
| $.expensive | 10|
| $.store.book[0].price | 8.95|
| $.store.book[-1].isbn | "0-395-19395-8"|
| $.store.book[0,1].price | [8.95, 12.99] |
| $.store.book[0:2].price | [8.95, 12.99, 8.99]|
| $.store.book[?(@.isbn)].price | [8.99, 22.99] |
| $.store.book[?(@.price > 10)].title | ["Sword of Honour", "The Lord of the Rings"]|
| $.store.book[?(@.price < $.expensive)].price | [8.95, 8.99] |
| $.store.book[:].price | [8.9.5, 12.99, 8.9.9, 22.99] |
| $.store.book[?(@.author =~ /(?i).*REES/)].author | "Nigel Rees" |
> Note: golang support regular expression flags in form of `(?imsU)pattern`

3
vendor/modules.txt vendored
View File

@@ -671,6 +671,9 @@ github.com/nxadm/tail/winfile
# github.com/oklog/ulid v1.3.1 => github.com/oklog/ulid v1.3.1
## explicit
github.com/oklog/ulid
# github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
## explicit
github.com/oliveagle/jsonpath
# github.com/onsi/ginkgo v1.16.4 => github.com/onsi/ginkgo v1.14.0
## explicit; go 1.13
github.com/onsi/ginkgo