Merge pull request #12432 from brendandburns/sort

Add field based sorting to the kubectl command line.
This commit is contained in:
CJ Cullen 2015-08-12 17:15:27 -07:00
commit caa549ff3f
21 changed files with 384 additions and 18 deletions

View File

@ -240,6 +240,7 @@ _kubectl_get()
flags+=("--output-version=")
flags+=("--selector=")
two_word_flags+=("-l")
flags+=("--sort-by=")
flags+=("--template=")
two_word_flags+=("-t")
flags+=("--watch")
@ -483,6 +484,7 @@ _kubectl_rolling-update()
flags+=("--output-version=")
flags+=("--poll-interval=")
flags+=("--rollback")
flags+=("--sort-by=")
flags+=("--template=")
two_word_flags+=("-t")
flags+=("--timeout=")
@ -645,6 +647,7 @@ _kubectl_run()
flags+=("--replicas=")
two_word_flags+=("-r")
flags+=("--restart=")
flags+=("--sort-by=")
flags+=("--stdin")
flags+=("-i")
flags+=("--template=")
@ -722,6 +725,7 @@ _kubectl_expose()
flags+=("--public-ip=")
flags+=("--selector=")
flags+=("--session-affinity=")
flags+=("--sort-by=")
flags+=("--target-port=")
flags+=("--template=")
two_word_flags+=("-t")
@ -753,6 +757,7 @@ _kubectl_label()
flags+=("--resource-version=")
flags+=("--selector=")
two_word_flags+=("-l")
flags+=("--sort-by=")
flags+=("--template=")
two_word_flags+=("-t")
@ -779,6 +784,7 @@ _kubectl_annotate()
flags+=("--output-version=")
flags+=("--overwrite")
flags+=("--resource-version=")
flags+=("--sort-by=")
flags+=("--template=")
two_word_flags+=("-t")
@ -806,6 +812,7 @@ _kubectl_config_view()
two_word_flags+=("-o")
flags+=("--output-version=")
flags+=("--raw")
flags+=("--sort-by=")
flags+=("--template=")
two_word_flags+=("-t")

View File

@ -57,6 +57,10 @@ resourcequotas (quota) or secrets.
\fB\-\-resource\-version\fP=""
If non\-empty, the annotation update will only succeed if this is the current resource\-version for the object. Only valid when specifying a single resource.
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-t\fP, \fB\-\-template\fP=""
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [

View File

@ -52,6 +52,10 @@ You can use \-\-output=template \-\-template=TEMPLATE to extract specific values
\fB\-\-raw\fP=false
display raw byte data
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-t\fP, \fB\-\-template\fP=""
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [

View File

@ -90,6 +90,10 @@ re\-use the labels from the resource it exposes.
\fB\-\-session\-affinity\fP=""
If non\-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-\-target\-port\fP=""
Name or number for the port on the container that the service should direct traffic to. Optional.

View File

@ -55,6 +55,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource(
\fB\-l\fP, \fB\-\-selector\fP=""
Selector (label query) to filter on
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-t\fP, \fB\-\-template\fP=""
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [

View File

@ -54,6 +54,10 @@ If \-\-resource\-version is specified, then updates will use this resource versi
\fB\-l\fP, \fB\-\-selector\fP=""
Selector (label query) to filter on
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-t\fP, \fB\-\-template\fP=""
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [

View File

@ -62,6 +62,10 @@ existing replication controller and overwrite at least one (common) label in its
\fB\-\-rollback\fP=false
If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-t\fP, \fB\-\-template\fP=""
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [

View File

@ -74,6 +74,10 @@ Creates a replication controller to manage the created container(s).
\fB\-\-restart\fP="Always"
The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and \-\-replicas must be 1. Default 'Always'
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-i\fP, \fB\-\-stdin\fP=false
Keep stdin open on the container(s) in the pod, even if nothing is attached.

View File

@ -85,6 +85,7 @@ $ kubectl annotate pods foo description-
--output-version="": Output the formatted object with the given version (default api-version).
--overwrite[=false]: If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.
--resource-version="": If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
```
@ -121,7 +122,7 @@ $ kubectl annotate pods foo description-
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-07 19:25:02.005112262 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-10 20:35:06.431305933 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_annotate.md?pixel)]()

View File

@ -67,6 +67,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2
-o, --output="": Output format. One of: json|yaml|template|templatefile|wide.
--output-version="": Output the formatted object with the given version (default api-version).
--raw[=false]: display raw byte data
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
```
@ -103,7 +104,7 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
###### Auto generated by spf13/cobra at 2015-08-12 16:38:35.553558331 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.310054033 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_view.md?pixel)]()

View File

@ -84,6 +84,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
--public-ip="": Name of a public IP address to set for the service. The service will be assigned this IP in addition to its generated service IP.
--selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller.
--session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP'
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
--target-port="": Name or number for the port on the container that the service should direct traffic to. Optional.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
--type="": Type for this service: ClusterIP, NodePort, or LoadBalancer. Default is 'ClusterIP' unless --create-external-load-balancer is specified.
@ -122,7 +123,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 17:00:51.073556749 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.308576759 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]()

View File

@ -87,6 +87,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7
-o, --output="": Output format. One of: json|yaml|template|templatefile|wide.
--output-version="": Output the formatted object with the given version (default api-version).
-l, --selector="": Selector (label query) to filter on
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
-w, --watch[=false]: After listing/getting the requested object, watch for changes.
--watch-only[=false]: Watch for changes to the requested object(s), without listing/getting first.
@ -125,7 +126,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 16:38:35.549908733 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.301023165 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_get.md?pixel)]()

View File

@ -79,6 +79,7 @@ $ kubectl label pods foo bar-
--overwrite[=false]: If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.
--resource-version="": If non-empty, the labels update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.
-l, --selector="": Selector (label query) to filter on
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
```
@ -115,7 +116,7 @@ $ kubectl label pods foo bar-
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 16:38:35.553249869 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.309176995 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_label.md?pixel)]()

View File

@ -79,6 +79,7 @@ $ kubectl rolling-update frontend --image=image:v2
--output-version="": Output the formatted object with the given version (default api-version).
--poll-interval=3s: Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
--rollback[=false]: If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
--timeout=5m0s: Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
--update-period=1m0s: Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
@ -117,7 +118,7 @@ $ kubectl rolling-update frontend --image=image:v2
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 16:38:35.55183074 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.305486289 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]()

View File

@ -78,6 +78,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { .
--port=-1: The port that this container exposes.
-r, --replicas=1: Number of replicas to create for this container. Default is 1.
--restart="Always": The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always'
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
-i, --stdin[=false]: Keep stdin open on the container(s) in the pod, even if nothing is attached.
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
--tty[=false]: Allocated a TTY for each container in the pod. Because -t is currently shorthand for --template, -t is not supported for --tty. This shorthand is deprecated and we expect to adopt -t for --tty soon.
@ -116,7 +117,7 @@ $ kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { .
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra at 2015-08-12 16:38:35.552754871 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.307766241 +0000 UTC
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]()

View File

@ -238,6 +238,7 @@ session-affinity
shutdown-fd
shutdown-fifo
skip-munges
sort-by
source-file
ssh-keyfile
ssh-user

View File

@ -471,6 +471,7 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
if err != nil {
return nil, err
}
printer = maybeWrapSortingPrinter(cmd, printer)
}
return printer, nil
}

View File

@ -32,6 +32,7 @@ func AddPrinterFlags(cmd *cobra.Command) {
cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).")
cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers.")
cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]")
cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.Name'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
}
// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
@ -86,5 +87,21 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error
outputFormat = "template"
}
return kubectl.GetPrinter(outputFormat, templateFile)
printer, generic, err := kubectl.GetPrinter(outputFormat, templateFile)
if err != nil {
return nil, generic, err
}
return maybeWrapSortingPrinter(cmd, printer), generic, nil
}
func maybeWrapSortingPrinter(cmd *cobra.Command, printer kubectl.ResourcePrinter) kubectl.ResourcePrinter {
sorting := GetFlagString(cmd, "sort-by")
if len(sorting) != 0 {
return &kubectl.SortingPrinter{
Delegate: printer,
SortField: fmt.Sprintf("{%s}", sorting),
}
}
return printer
}

View File

@ -0,0 +1,123 @@
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"fmt"
"io"
"reflect"
"sort"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/jsonpath"
)
// Sorting printer sorts list types before delegating to another printer.
// Non-list types are simply passed through
type SortingPrinter struct {
SortField string
Delegate ResourcePrinter
}
func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
if !runtime.IsListType(obj) {
fmt.Fprintf(out, "Not a list, skipping: %#v\n", obj)
return s.Delegate.PrintObj(obj, out)
}
if err := s.sortObj(obj); err != nil {
return err
}
return s.Delegate.PrintObj(obj, out)
}
func (s *SortingPrinter) sortObj(obj runtime.Object) error {
objs, err := runtime.ExtractList(obj)
if err != nil {
return err
}
if len(objs) == 0 {
return nil
}
parser := jsonpath.New("sorting")
parser.Parse(s.SortField)
values, err := parser.FindResults(reflect.ValueOf(objs[0]).Elem().Interface())
if err != nil {
return err
}
if len(values) == 0 {
return fmt.Errorf("couldn't find any field with path: %s", s.SortField)
}
sorter := &RuntimeSort{
field: s.SortField,
objs: objs,
}
sort.Sort(sorter)
runtime.SetList(obj, sorter.objs)
return nil
}
// RuntimeSort is an implementation of the golang sort interface that knows how to sort
// lists of runtime.Object
type RuntimeSort struct {
field string
objs []runtime.Object
}
func (r *RuntimeSort) Len() int {
return len(r.objs)
}
func (r *RuntimeSort) Swap(i, j int) {
r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
}
func (r *RuntimeSort) Less(i, j int) bool {
iObj := r.objs[i]
jObj := r.objs[j]
parser := jsonpath.New("sorting")
parser.Parse(r.field)
iValues, err := parser.FindResults(reflect.ValueOf(iObj).Elem().Interface())
if err != nil {
glog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
}
jValues, err := parser.FindResults(reflect.ValueOf(jObj).Elem().Interface())
if err != nil {
glog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
}
iField := iValues[0][0]
jField := jValues[0][0]
switch iField.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return iField.Int() < jField.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return iField.Uint() < jField.Uint()
case reflect.Float32, reflect.Float64:
return iField.Float() < jField.Float()
case reflect.String:
return iField.String() < jField.String()
default:
glog.Fatalf("Field %s in %v is an unsortable type: %s", r.field, iObj, iField.Kind().String())
}
// default to preserving order
return i < j
}

View File

@ -0,0 +1,171 @@
/*
Copyright 2014 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
func TestSortingPrinter(t *testing.T) {
tests := []struct {
obj runtime.Object
sort runtime.Object
field string
name string
}{
{
name: "in-order-already",
obj: &api.PodList{
Items: []api.Pod{
{
ObjectMeta: api.ObjectMeta{
Name: "a",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "b",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "c",
},
},
},
},
sort: &api.PodList{
Items: []api.Pod{
{
ObjectMeta: api.ObjectMeta{
Name: "a",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "b",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "c",
},
},
},
},
field: "{.ObjectMeta.Name}",
},
{
name: "reverse-order",
obj: &api.PodList{
Items: []api.Pod{
{
ObjectMeta: api.ObjectMeta{
Name: "b",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "c",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "a",
},
},
},
},
sort: &api.PodList{
Items: []api.Pod{
{
ObjectMeta: api.ObjectMeta{
Name: "a",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "b",
},
},
{
ObjectMeta: api.ObjectMeta{
Name: "c",
},
},
},
},
field: "{.ObjectMeta.Name}",
},
{
name: "random-order-numbers",
obj: &api.ReplicationControllerList{
Items: []api.ReplicationController{
{
Spec: api.ReplicationControllerSpec{
Replicas: 5,
},
},
{
Spec: api.ReplicationControllerSpec{
Replicas: 1,
},
},
{
Spec: api.ReplicationControllerSpec{
Replicas: 9,
},
},
},
},
sort: &api.ReplicationControllerList{
Items: []api.ReplicationController{
{
Spec: api.ReplicationControllerSpec{
Replicas: 1,
},
},
{
Spec: api.ReplicationControllerSpec{
Replicas: 5,
},
},
{
Spec: api.ReplicationControllerSpec{
Replicas: 9,
},
},
},
},
field: "{.Spec.Replicas}",
},
}
for _, test := range tests {
sort := &SortingPrinter{SortField: test.field}
if err := sort.sortObj(test.obj); err != nil {
t.Errorf("unexpected error: %v (%s)", err, test.name)
continue
}
if !reflect.DeepEqual(test.obj, test.sort) {
t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v", test.name, test.sort, test.obj)
}
}
}

View File

@ -53,17 +53,31 @@ func (j *JSONPath) Parse(text string) (err error) {
// Execute bounds data into template and write the result
func (j *JSONPath) Execute(wr io.Writer, data interface{}) error {
fullResults, err := j.FindResults(data)
if err != nil {
return err
}
for ix := range fullResults {
if err := j.PrintResults(wr, fullResults[ix]); err != nil {
return err
}
}
return nil
}
func (j *JSONPath) FindResults(data interface{}) ([][]reflect.Value, error) {
if j.parser == nil {
return fmt.Errorf("%s is an incomplete jsonpath template", j.name)
return nil, fmt.Errorf("%s is an incomplete jsonpath template", j.name)
}
j.cur = []reflect.Value{reflect.ValueOf(data)}
nodes := j.parser.Root.Nodes
fullResult := [][]reflect.Value{}
for i := 0; i < len(nodes); i++ {
node := nodes[i]
results, err := j.walk(j.cur, node)
if err != nil {
return err
return nil, err
}
//encounter an end node, break the current block
@ -80,20 +94,17 @@ func (j *JSONPath) Execute(wr io.Writer, data interface{}) error {
if k == len(results)-1 {
j.inRange -= 1
}
err := j.Execute(wr, value.Interface())
nextResults, err := j.FindResults(value.Interface())
if err != nil {
return err
return nil, err
}
fullResult = append(fullResult, nextResults...)
}
break
}
err = j.PrintResults(wr, results)
if err != nil {
return err
}
fullResult = append(fullResult, results)
}
return nil
return fullResult, nil
}
// PrintResults write the results into writer