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 }