github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/k8s/jsonpath/jsonpath.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package jsonpath
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"reflect"
    24  	"strings"
    25  
    26  	"k8s.io/client-go/third_party/forked/golang/template"
    27  )
    28  
    29  type JSONPath struct {
    30  	name       string
    31  	parser     *Parser
    32  	stack      [][]Value // push and pop values in different scopes
    33  	cur        []Value   // current scope values
    34  	beginRange int
    35  	inRange    int
    36  	endRange   int
    37  
    38  	allowMissingKeys bool
    39  }
    40  
    41  // New creates a new JSONPath with the given name.
    42  func New(name string) *JSONPath {
    43  	return &JSONPath{
    44  		name:       name,
    45  		beginRange: 0,
    46  		inRange:    0,
    47  		endRange:   0,
    48  	}
    49  }
    50  
    51  // AllowMissingKeys allows a caller to specify whether they want an error if a field or map key
    52  // cannot be located, or simply an empty result. The receiver is returned for chaining.
    53  func (j *JSONPath) AllowMissingKeys(allow bool) *JSONPath {
    54  	j.allowMissingKeys = allow
    55  	return j
    56  }
    57  
    58  // Parse parses the given template and returns an error.
    59  func (j *JSONPath) Parse(text string) error {
    60  	var err error
    61  	j.parser, err = Parse(j.name, text)
    62  	return err
    63  }
    64  
    65  // Execute bounds data into template and writes the result.
    66  func (j *JSONPath) Execute(wr io.Writer, data interface{}) error {
    67  	fullResults, err := j.FindResults(data)
    68  	if err != nil {
    69  		return err
    70  	}
    71  	for ix := range fullResults {
    72  		if err := j.PrintResults(wr, fullResults[ix]); err != nil {
    73  			return err
    74  		}
    75  	}
    76  	return nil
    77  }
    78  
    79  func (j *JSONPath) FindResults(data interface{}) ([][]Value, error) {
    80  	if j.parser == nil {
    81  		return nil, fmt.Errorf("%s is an incomplete jsonpath template", j.name)
    82  	}
    83  
    84  	j.cur = []Value{ValueOf(data)}
    85  	nodes := j.parser.Root.Nodes
    86  	fullResult := [][]Value{}
    87  	for i := 0; i < len(nodes); i++ {
    88  		node := nodes[i]
    89  		results, err := j.walk(j.cur, node)
    90  		if err != nil {
    91  			return nil, err
    92  		}
    93  
    94  		// encounter an end node, break the current block
    95  		if j.endRange > 0 && j.endRange <= j.inRange {
    96  			j.endRange--
    97  			break
    98  		}
    99  		// encounter a range node, start a range loop
   100  		if j.beginRange > 0 {
   101  			j.beginRange--
   102  			j.inRange++
   103  			for k, value := range results {
   104  				j.parser.Root.Nodes = nodes[i+1:]
   105  				if k == len(results)-1 {
   106  					j.inRange--
   107  				}
   108  				nextResults, err := j.FindResults(value.Interface())
   109  				if err != nil {
   110  					return nil, err
   111  				}
   112  				fullResult = append(fullResult, nextResults...)
   113  			}
   114  			break
   115  		}
   116  		fullResult = append(fullResult, results)
   117  	}
   118  	return fullResult, nil
   119  }
   120  
   121  // PrintResults writes the results into writer
   122  func (j *JSONPath) PrintResults(wr io.Writer, results []Value) error {
   123  	for i, r := range results {
   124  		text, err := j.evalToText(r)
   125  		if err != nil {
   126  			return err
   127  		}
   128  		if i != len(results)-1 {
   129  			text = append(text, ' ')
   130  		}
   131  		if _, err = wr.Write(text); err != nil {
   132  			return err
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  // walk visits tree rooted at the given node in DFS order
   139  func (j *JSONPath) walk(value []Value, node Node) ([]Value, error) {
   140  	switch node := node.(type) {
   141  	case *ListNode:
   142  		return j.evalList(value, node)
   143  	case *TextNode:
   144  		return []Value{ValueOf(node.Text)}, nil
   145  	case *FieldNode:
   146  		return j.evalField(value, node)
   147  	case *ArrayNode:
   148  		return j.evalArray(value, node)
   149  	case *FilterNode:
   150  		return j.evalFilter(value, node)
   151  	case *IntNode:
   152  		return j.evalInt(value, node)
   153  	case *BoolNode:
   154  		return j.evalBool(value, node)
   155  	case *FloatNode:
   156  		return j.evalFloat(value, node)
   157  	case *WildcardNode:
   158  		return j.evalWildcard(value, node)
   159  	case *RecursiveNode:
   160  		return j.evalRecursive(value, node)
   161  	case *UnionNode:
   162  		return j.evalUnion(value, node)
   163  	case *IdentifierNode:
   164  		return j.evalIdentifier(value, node)
   165  	default:
   166  		return value, fmt.Errorf("unexpected Node %v", node)
   167  	}
   168  }
   169  
   170  // evalInt evaluates IntNode
   171  func (j *JSONPath) evalInt(input []Value, node *IntNode) ([]Value, error) {
   172  	result := make([]Value, len(input))
   173  	for i := range input {
   174  		result[i] = ValueOf(node.Value)
   175  	}
   176  	return result, nil
   177  }
   178  
   179  // evalFloat evaluates FloatNode
   180  func (j *JSONPath) evalFloat(input []Value, node *FloatNode) ([]Value, error) {
   181  	result := make([]Value, len(input))
   182  	for i := range input {
   183  		result[i] = ValueOf(node.Value)
   184  	}
   185  	return result, nil
   186  }
   187  
   188  // evalBool evaluates BoolNode
   189  func (j *JSONPath) evalBool(input []Value, node *BoolNode) ([]Value, error) {
   190  	result := make([]Value, len(input))
   191  	for i := range input {
   192  		result[i] = ValueOf(node.Value)
   193  	}
   194  	return result, nil
   195  }
   196  
   197  // evalList evaluates ListNode
   198  func (j *JSONPath) evalList(value []Value, node *ListNode) ([]Value, error) {
   199  	var err error
   200  	curValue := value
   201  	for _, node := range node.Nodes {
   202  		curValue, err = j.walk(curValue, node)
   203  		if err != nil {
   204  			return curValue, err
   205  		}
   206  	}
   207  	return curValue, nil
   208  }
   209  
   210  // evalIdentifier evaluates IdentifierNode
   211  func (j *JSONPath) evalIdentifier(input []Value, node *IdentifierNode) ([]Value, error) {
   212  	results := []Value{}
   213  	switch node.Name {
   214  	case "range":
   215  		j.stack = append(j.stack, j.cur)
   216  		j.beginRange++
   217  		results = input
   218  	case "end":
   219  		if j.endRange < j.inRange { // inside a loop, break the current block
   220  			j.endRange++
   221  			break
   222  		}
   223  		// the loop is about to end, pop value and continue the following execution
   224  		if len(j.stack) > 0 {
   225  			j.cur, j.stack = j.stack[len(j.stack)-1], j.stack[:len(j.stack)-1]
   226  		} else {
   227  			return results, fmt.Errorf("not in range, nothing to end")
   228  		}
   229  	default:
   230  		return input, fmt.Errorf("unrecognized identifier %v", node.Name)
   231  	}
   232  	return results, nil
   233  }
   234  
   235  // evalArray evaluates ArrayNode
   236  func (j *JSONPath) evalArray(input []Value, node *ArrayNode) ([]Value, error) {
   237  	result := []Value{}
   238  	for _, value := range input {
   239  
   240  		value, isNil := template.Indirect(value.Value)
   241  		if isNil {
   242  			continue
   243  		}
   244  		if value.Kind() != reflect.Array && value.Kind() != reflect.Slice {
   245  			return input, fmt.Errorf("%v is not array or slice", value.Type())
   246  		}
   247  		params := node.Params
   248  		if !params[0].Known {
   249  			params[0].Value = 0
   250  		}
   251  		if params[0].Value < 0 {
   252  			params[0].Value += value.Len()
   253  		}
   254  		if !params[1].Known {
   255  			params[1].Value = value.Len()
   256  		}
   257  
   258  		if params[1].Value < 0 || (params[1].Value == 0 && params[1].Derived) {
   259  			params[1].Value += value.Len()
   260  		}
   261  		sliceLength := value.Len()
   262  		if params[1].Value != params[0].Value { // if you're requesting zero elements, allow it through.
   263  			if params[0].Value >= sliceLength || params[0].Value < 0 {
   264  				return input, fmt.Errorf("array index out of bounds: index %d, length %d", params[0].Value, sliceLength)
   265  			}
   266  			if params[1].Value > sliceLength || params[1].Value < 0 {
   267  				return input, fmt.Errorf("array index out of bounds: index %d, length %d", params[1].Value-1, sliceLength)
   268  			}
   269  			if params[0].Value > params[1].Value {
   270  				return input, fmt.Errorf("starting index %d is greater than ending index %d", params[0].Value, params[1].Value)
   271  			}
   272  		} else {
   273  			return result, nil
   274  		}
   275  
   276  		value = value.Slice(params[0].Value, params[1].Value)
   277  
   278  		step := 1
   279  		if params[2].Known {
   280  			if params[2].Value <= 0 {
   281  				return input, fmt.Errorf("step must be > 0")
   282  			}
   283  			step = params[2].Value
   284  		}
   285  		for i := 0; i < value.Len(); i += step {
   286  			result = append(result, Wrap(value.Index(i)))
   287  		}
   288  	}
   289  	return result, nil
   290  }
   291  
   292  // evalUnion evaluates UnionNode
   293  func (j *JSONPath) evalUnion(input []Value, node *UnionNode) ([]Value, error) {
   294  	result := []Value{}
   295  	for _, listNode := range node.Nodes {
   296  		temp, err := j.evalList(input, listNode)
   297  		if err != nil {
   298  			return input, err
   299  		}
   300  		result = append(result, temp...)
   301  	}
   302  	return result, nil
   303  }
   304  
   305  func (j *JSONPath) findFieldInValue(value *Value, node *FieldNode) (Value, error) {
   306  	t := value.Type()
   307  	var inlineValue *Value
   308  	for ix := 0; ix < t.NumField(); ix++ {
   309  		f := t.Field(ix)
   310  		jsonTag := f.Tag.Get("json")
   311  		parts := strings.Split(jsonTag, ",")
   312  		if len(parts) == 0 {
   313  			continue
   314  		}
   315  		if parts[0] == node.Value {
   316  			return Wrap(value.Field(ix)), nil
   317  		}
   318  		if len(parts[0]) == 0 {
   319  			val := Wrap(value.Field(ix))
   320  			inlineValue = &val
   321  		}
   322  	}
   323  	if inlineValue != nil {
   324  		if inlineValue.Kind() == reflect.Struct {
   325  			// handle 'inline'
   326  			match, err := j.findFieldInValue(inlineValue, node)
   327  			if err != nil {
   328  				return Value{}, err
   329  			}
   330  			if match.IsValid() {
   331  				return match, nil
   332  			}
   333  		}
   334  	}
   335  	return Wrap(value.FieldByName(node.Value)), nil
   336  }
   337  
   338  // evalField evaluates field of struct or key of map.
   339  func (j *JSONPath) evalField(input []Value, node *FieldNode) ([]Value, error) {
   340  	results := []Value{}
   341  	// If there's no input, there's no output
   342  	if len(input) == 0 {
   343  		return results, nil
   344  	}
   345  	for _, value := range input {
   346  		var result Value
   347  		value, isNil := template.Indirect(value.Value)
   348  		if isNil {
   349  			continue
   350  		}
   351  
   352  		if value.Kind() == reflect.Struct {
   353  			var err error
   354  			if result, err = j.findFieldInValue(&Value{Value: value}, node); err != nil {
   355  				return nil, err
   356  			}
   357  		} else if value.Kind() == reflect.Map {
   358  			mapKeyType := value.Type().Key()
   359  			nodeValue := ValueOf(node.Value)
   360  			// node value type must be convertible to map key type
   361  			if !nodeValue.Type().ConvertibleTo(mapKeyType) {
   362  				return results, fmt.Errorf("%s is not convertible to %s", nodeValue, mapKeyType)
   363  			}
   364  
   365  			mapKey := nodeValue.Convert(mapKeyType)
   366  			result = Wrap(value.MapIndex(mapKey))
   367  			result.parentMap = value
   368  			result.parentMapKey = mapKey
   369  		}
   370  		if result.IsValid() {
   371  			results = append(results, result)
   372  		}
   373  	}
   374  	if len(results) == 0 {
   375  		if j.allowMissingKeys {
   376  			return results, nil
   377  		}
   378  		return results, fmt.Errorf("%s is not found", node.Value)
   379  	}
   380  	return results, nil
   381  }
   382  
   383  // evalWildcard extracts all contents of the given value
   384  func (j *JSONPath) evalWildcard(input []Value, node *WildcardNode) ([]Value, error) {
   385  	results := []Value{}
   386  	for _, value := range input {
   387  		value, isNil := template.Indirect(value.Value)
   388  		if isNil {
   389  			continue
   390  		}
   391  
   392  		kind := value.Kind()
   393  		if kind == reflect.Struct {
   394  			for i := 0; i < value.NumField(); i++ {
   395  				results = append(results, Wrap(value.Field(i)))
   396  			}
   397  		} else if kind == reflect.Map {
   398  			for _, key := range value.MapKeys() {
   399  				results = append(results, Wrap(value.MapIndex(key)))
   400  			}
   401  		} else if kind == reflect.Array || kind == reflect.Slice || kind == reflect.String {
   402  			for i := 0; i < value.Len(); i++ {
   403  				results = append(results, Wrap(value.Index(i)))
   404  			}
   405  		}
   406  	}
   407  	return results, nil
   408  }
   409  
   410  // evalRecursive visits the given value recursively and pushes all of them to result
   411  func (j *JSONPath) evalRecursive(input []Value, node *RecursiveNode) ([]Value, error) {
   412  	result := []Value{}
   413  	for _, value := range input {
   414  		results := []Value{}
   415  		value, isNil := template.Indirect(value.Value)
   416  		if isNil {
   417  			continue
   418  		}
   419  
   420  		kind := value.Kind()
   421  		if kind == reflect.Struct {
   422  			for i := 0; i < value.NumField(); i++ {
   423  				results = append(results, Wrap(value.Field(i)))
   424  			}
   425  		} else if kind == reflect.Map {
   426  			for _, key := range value.MapKeys() {
   427  				results = append(results, Wrap(value.MapIndex(key)))
   428  			}
   429  		} else if kind == reflect.Array || kind == reflect.Slice || kind == reflect.String {
   430  			for i := 0; i < value.Len(); i++ {
   431  				results = append(results, Wrap(value.Index(i)))
   432  			}
   433  		}
   434  		if len(results) != 0 {
   435  			result = append(result, Wrap(value))
   436  			output, err := j.evalRecursive(results, node)
   437  			if err != nil {
   438  				return result, err
   439  			}
   440  			result = append(result, output...)
   441  		}
   442  	}
   443  	return result, nil
   444  }
   445  
   446  // evalFilter filters array according to FilterNode
   447  func (j *JSONPath) evalFilter(input []Value, node *FilterNode) ([]Value, error) {
   448  	results := []Value{}
   449  	for _, value := range input {
   450  		valueInner, _ := template.Indirect(value.Value)
   451  		value = Wrap(valueInner)
   452  
   453  		if value.Kind() != reflect.Array && value.Kind() != reflect.Slice {
   454  			return input, fmt.Errorf("%v is not array or slice and cannot be filtered", value)
   455  		}
   456  		for i := 0; i < value.Len(); i++ {
   457  			temp := []Value{Wrap(value.Index(i))}
   458  			lefts, err := j.evalList(temp, node.Left)
   459  
   460  			// case exists
   461  			if node.Operator == "exists" {
   462  				if len(lefts) > 0 {
   463  					results = append(results, Wrap(value.Index(i)))
   464  				}
   465  				continue
   466  			}
   467  
   468  			if err != nil {
   469  				return input, err
   470  			}
   471  
   472  			var left, right interface{}
   473  			switch {
   474  			case len(lefts) == 0:
   475  				continue
   476  			case len(lefts) > 1:
   477  				return input, fmt.Errorf("can only compare one element at a time")
   478  			}
   479  			left = lefts[0].Interface()
   480  
   481  			rights, err := j.evalList(temp, node.Right)
   482  			if err != nil {
   483  				return input, err
   484  			}
   485  			switch {
   486  			case len(rights) == 0:
   487  				continue
   488  			case len(rights) > 1:
   489  				return input, fmt.Errorf("can only compare one element at a time")
   490  			}
   491  			right = rights[0].Interface()
   492  
   493  			pass := false
   494  			switch node.Operator {
   495  			case "<":
   496  				pass, err = template.Less(left, right)
   497  			case ">":
   498  				pass, err = template.Greater(left, right)
   499  			case "==":
   500  				pass, err = template.Equal(left, right)
   501  			case "!=":
   502  				pass, err = template.NotEqual(left, right)
   503  			case "<=":
   504  				pass, err = template.LessEqual(left, right)
   505  			case ">=":
   506  				pass, err = template.GreaterEqual(left, right)
   507  			default:
   508  				return results, fmt.Errorf("unrecognized filter operator %s", node.Operator)
   509  			}
   510  			if err != nil {
   511  				return results, err
   512  			}
   513  			if pass {
   514  				results = append(results, Wrap(value.Index(i)))
   515  			}
   516  		}
   517  	}
   518  	return results, nil
   519  }
   520  
   521  // evalToText translates reflect value to corresponding text
   522  func (j *JSONPath) evalToText(v Value) ([]byte, error) {
   523  	iface, ok := template.PrintableValue(v.Value)
   524  	if !ok {
   525  		return nil, fmt.Errorf("can't print type %s", v.Type())
   526  	}
   527  	var buffer bytes.Buffer
   528  	fmt.Fprint(&buffer, iface)
   529  	return buffer.Bytes(), nil
   530  }