Change:
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks! If the matter is security related, please disclose it privately via --What happened: (or in my instance) with does not correctly handle nested range statements. For example, one would expect that the following: would result in a list being output similar to this: but instead, results in a list being output like this: What you expected to happen: Nested range statements complete in the order in which they are specified in jsonpath query. How to reproduce it (as minimally and precisely as possible): Anything else we need to know?: Environment: Kubernetes version (use ): 1.11.0 Cloud provider or hardware configuration: OpenShift 3.11.141 on vSphere 6.5 OS (e.g: ): RHEL 7.7 Kernel (e.g. ): 3.10.0 Install tools:
/sig cli
/area kubectl
/assign
/sig api-machinery
/remove-sig cli
/remove-area cli /area api
Those labels are not set on the issue: <detailsIn response to : /remove-area cli /area api Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/remove-area kubectl

Discussion:
type JSONPath struct { name       string parser     *Parser <del> stack      [][]reflect.Value // push and pop values in different scopes cur        []reflect.Value   // current scope values </del> beginRange int inRange    int endRange   int <ins> lastEndNode *Node </ins> allowMissingKeys bool }

return nil, fmt.Errorf("%s is an incomplete jsonpath template", j.name) } <del> j.cur = []reflect.Value{reflect.ValueOf(data)} </del> <ins> cur := []reflect.Value{reflect.ValueOf(data)} </ins> nodes := j.parser.Root.Nodes fullResult := [][]reflect.Value{} for i := 0; i < len(nodes); i++ { node := nodes[i] <del> results, err := j.walk(j.cur, node) </del> <ins> results, err := j.walk(cur, node) </ins> if err != nil { return nil, err }

// encounter an end node, break the current block if j.endRange > 0 && j.endRange <= j.inRange { j.endRange-- <ins> j.lastEndNode = &nodes[i] </ins> break } // encounter a range node, start a range loop if j.beginRange > 0 { j.beginRange-- j.inRange++ <del> for k, value := range results { </del> <ins> for _, value := range results { </ins> j.parser.Root.Nodes = nodes[i+1:] <del> if k == len(results)-1 { j.inRange-- } </del> nextResults, err := j.FindResults(value.Interface()) if err != nil { return nil, err } fullResult = append(fullResult, nextResults...) } <del> break </del> <ins> j.inRange-- // Fast forward to resume processing after the most recent end node that was encountered for k := i + 1; k < len(nodes); k++ { if &nodes[k] == j.lastEndNode { i = k break } } continue </ins> } fullResult = append(fullResult, results) }

results := []reflect.Value{} switch node.Name { case "range": <del> j.stack = append(j.stack, j.cur) </del> j.beginRange++ results = input case "end": <del> if j.endRange < j.inRange { // inside a loop, break the current block </del> <ins> if j.inRange > 0 { </ins> j.endRange++ <del> break } // the loop is about to end, pop value and continue the following execution if len(j.stack) > 0 { j.cur, j.stack = j.stack[len(j.stack)-1], j.stack[:len(j.stack)-1] </del> } else { return results, fmt.Errorf("not in range, nothing to end") }

testJSONPathSortOutput(randomPrintOrderTests, t) } <ins> func TestNestedRanges(t *testing.T) { var input = []byte(`{ "items": [ { "metadata": { "name": "pod1" }, "spec": { "containers": [ { "name": "foo", "another": [ { "name": "value1" }, { "name": "value2" } ] }, { "name": "bar", "another": [ { "name": "value1" }, { "name": "value2" } ] } ] } }, { "metadata": { "name": "pod2" }, "spec": { "containers": [ { "name": "baz", "another": [ { "name": "value1" }, { "name": "value2" } ] } ] } } ] }`) var data interface{} err := json.Unmarshal(input, &data) if err != nil { t.Error(err) } testJSONPath( []jsonpathTest{ { "nested range with a trailing newline", `{range .items[*]}` + `{.metadata.name}` + `{":"}` + `{range @.spec.containers[*]}` + `{.name}` + `{","}` + `{end}` + `{"+"}` + `{end}`, data, "pod1:foo,bar,+pod2:baz,+", false, }, }, false, t, ) testJSONPath( []jsonpathTest{ { "nested range with a trailing character within another nested range with a trailing newline", `{range .items[*]}` + `{.metadata.name}` + `{"~"}` + `{range @.spec.containers[*]}` + `{.name}` + `{":"}` + `{range @.another[*]}` + `{.name}` + `{","}` + `{end}` + `{"+"}` + `{end}` + `{"#"}` + `{end}`, data, "pod1~foo:value1,value2,+bar:value1,value2,+#pod2~baz:value1,value2,+#", false, }, }, false, t, ) testJSONPath( []jsonpathTest{ { "two nested ranges at the same level with a trailing newline", `{range .items[*]}` + `{.metadata.name}` + `{"t"}` + `{range @.spec.containers[*]}` + `{.name}` + `{" "}` + `{end}` + `{"t"}` + `{range @.spec.containers[*]}` + `{.name}` + `{" "}` + `{end}` + `{"n"}` + `{end}`, data, "pod1tfoo bar tfoo bar npod2tbaz tbaz n", false, }, }, false, t, ) } </ins> func TestFilterPartialMatchesSometimesMissingAnnotations(t *testing.T) { // for https://issues.k8s.io/45546 var input = []byte(`{