Commit 613ada4c authored by Andy Goldstein's avatar Andy Goldstein

Fix kubectl get -f <file> -o <nondefault printer>

Fix kubectl get -f <file> -o <nondefault printer> so it prints all the objects in the file, instead of just the first one. Also add a test for this feature.
parent ba611194
......@@ -1072,6 +1072,20 @@ run_kubectl_get_tests() {
# cleanup
kubectl delete pods valid-pod "${kube_flags[@]}"
### Test 'kubectl get -f <file> -o <non default printer>' prints all the items in the file's list
# Pre-condition: no POD exists
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" ''
# Command
kubectl create -f test/fixtures/doc-yaml/user-guide/multi-pod.yaml "${kube_flags[@]}"
# Post-condition: PODs redis-master and redis-proxy exist
# Check that all items in the list are printed
output_message=$(kubectl get -f test/fixtures/doc-yaml/user-guide/multi-pod.yaml -o jsonpath="{..metadata.name}" "${kube_flags[@]}")
kube::test::if_has_string "${output_message}" "redis-master redis-proxy"
# cleanup
kubectl delete pods redis-master redis-proxy "${kube_flags[@]}"
}
run_kubectl_request_timeout_tests() {
......
......@@ -197,14 +197,14 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
return err
}
var singularResource bool
r.IntoSingular(&singularResource)
var singleItemImpliedResource bool
r.IntoSingleItemImplied(&singleItemImpliedResource)
// only apply resource version locking on a single resource.
// we must perform this check after o.builder.Do() as
// []o.resources can not not accurately return the proper number
// of resources when they are not passed in "resource/name" format.
if !singularResource && len(o.resourceVersion) > 0 {
if !singleItemImpliedResource && len(o.resourceVersion) > 0 {
return fmt.Errorf("--resource-version may only be used with a single resource")
}
......
......@@ -162,8 +162,8 @@ func (o *ConvertOptions) RunConvert() error {
return err
}
singular := false
infos, err := r.IntoSingular(&singular).Infos()
singleItemImplied := false
infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
if err != nil {
return err
}
......@@ -172,7 +172,7 @@ func (o *ConvertOptions) RunConvert() error {
return fmt.Errorf("no objects passed to convert")
}
objects, err := resource.AsVersionedObject(infos, !singular, o.outputVersion, o.encoder)
objects, err := resource.AsVersionedObject(infos, !singleItemImplied, o.outputVersion, o.encoder)
if err != nil {
return err
}
......
......@@ -307,10 +307,10 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
// 2. if there is a single item and that item is a list, leave it as its specific list
// 3. if there is a single item and it is not a a list, leave it as a single item
var errs []error
singular := false
infos, err := r.IntoSingular(&singular).Infos()
singleItemImplied := false
infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
if err != nil {
if singular {
if singleItemImplied {
return err
}
errs = append(errs, err)
......@@ -325,9 +325,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
}
var obj runtime.Object
if singular {
obj = infos[0].Object
} else {
if !singleItemImplied || len(infos) > 1 {
// we have more than one item, so coerce all items into a list
list := &unstructured.UnstructuredList{
Object: map[string]interface{}{
......@@ -340,6 +338,8 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
list.Items = append(list.Items, info.Object.(*unstructured.Unstructured))
}
obj = list
} else {
obj = infos[0].Object
}
isList := meta.IsListType(obj)
......
......@@ -187,7 +187,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
Latest()
}
one := false
r := b.Do().IntoSingular(&one)
r := b.Do().IntoSingleItemImplied(&one)
if err := r.Err(); err != nil {
return err
}
......
......@@ -72,7 +72,7 @@ type Builder struct {
singleResourceType bool
continueOnError bool
singular bool
singleItemImplied bool
export bool
......@@ -139,7 +139,7 @@ func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *Filename
b.URL(defaultHttpGetAttempts, url)
default:
if !recursive {
b.singular = true
b.singleItemImplied = true
}
b.Path(recursive, s)
}
......@@ -600,21 +600,21 @@ func (b *Builder) visitBySelector() *Result {
}
func (b *Builder) visitByResource() *Result {
// if b.singular is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singular or not
isSingular := b.singular
if !isSingular {
isSingular = len(b.resourceTuples) == 1
// if b.singleItemImplied is false, this could be by default, so double-check length
// of resourceTuples to determine if in fact it is singleItemImplied or not
isSingleItemImplied := b.singleItemImplied
if !isSingleItemImplied {
isSingleItemImplied = len(b.resourceTuples) == 1
}
if len(b.resources) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("you may not specify individual resources and bulk resources in the same call")}
}
// retrieve one client for each resource
mappings, err := b.resourceTupleMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
return &Result{singleItemImplied: isSingleItemImplied, err: err}
}
clients := make(map[string]RESTClient)
for _, mapping := range mappings {
......@@ -633,12 +633,12 @@ func (b *Builder) visitByResource() *Result {
for _, tuple := range b.resourceTuples {
mapping, ok := mappings[tuple.Resource]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings)}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("resource %q is not recognized: %v", tuple.Resource, mappings)}
}
s := fmt.Sprintf("%s/%s", mapping.GroupVersionKind.GroupVersion().String(), mapping.Resource)
client, ok := clients[s]
if !ok {
return &Result{singular: isSingular, err: fmt.Errorf("could not find a client for resource %q", tuple.Resource)}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("could not find a client for resource %q", tuple.Resource)}
}
selectorNamespace := b.namespace
......@@ -650,7 +650,7 @@ func (b *Builder) visitByResource() *Result {
if b.allNamespace {
errMsg = "a resource cannot be retrieved by name across all namespaces"
}
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf(errMsg)}
}
}
......@@ -664,25 +664,25 @@ func (b *Builder) visitByResource() *Result {
} else {
visitors = VisitorList(items)
}
return &Result{singular: isSingular, visitor: visitors, sources: items}
return &Result{singleItemImplied: isSingleItemImplied, visitor: visitors, sources: items}
}
func (b *Builder) visitByName() *Result {
isSingular := len(b.names) == 1
isSingleItemImplied := len(b.names) == 1
if len(b.paths) != 0 {
return &Result{singular: isSingular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify a resource by arguments as well")}
}
if len(b.resources) == 0 {
return &Result{singular: isSingular, err: fmt.Errorf("you must provide a resource and a resource name together")}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("you must provide a resource and a resource name together")}
}
if len(b.resources) > 1 {
return &Result{singular: isSingular, err: fmt.Errorf("you must specify only one resource")}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf("you must specify only one resource")}
}
mappings, err := b.resourceMappings()
if err != nil {
return &Result{singular: isSingular, err: err}
return &Result{singleItemImplied: isSingleItemImplied, err: err}
}
mapping := mappings[0]
......@@ -700,7 +700,7 @@ func (b *Builder) visitByName() *Result {
if b.allNamespace {
errMsg = "a resource cannot be retrieved by name across all namespaces"
}
return &Result{singular: isSingular, err: fmt.Errorf(errMsg)}
return &Result{singleItemImplied: isSingleItemImplied, err: fmt.Errorf(errMsg)}
}
}
......@@ -709,13 +709,13 @@ func (b *Builder) visitByName() *Result {
info := NewInfo(client, mapping, selectorNamespace, name, b.export)
visitors = append(visitors, info)
}
return &Result{singular: isSingular, visitor: VisitorList(visitors), sources: visitors}
return &Result{singleItemImplied: isSingleItemImplied, visitor: VisitorList(visitors), sources: visitors}
}
func (b *Builder) visitByPaths() *Result {
singular := !b.dir && !b.stream && len(b.paths) == 1
singleItemImplied := !b.dir && !b.stream && len(b.paths) == 1
if len(b.resources) != 0 {
return &Result{singular: singular, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
return &Result{singleItemImplied: singleItemImplied, err: fmt.Errorf("when paths, URLs, or stdin is provided as input, you may not specify resource arguments as well")}
}
if len(b.names) != 0 {
return &Result{err: fmt.Errorf("name cannot be provided when a path is specified")}
......@@ -746,7 +746,7 @@ func (b *Builder) visitByPaths() *Result {
if b.selector != nil {
visitors = NewFilteredVisitor(visitors, FilterBySelector(b.selector))
}
return &Result{singular: singular, visitor: visitors, sources: b.paths}
return &Result{singleItemImplied: singleItemImplied, visitor: visitors, sources: b.paths}
}
// Do returns a Result object with a Visitor for the resources identified by the Builder.
......
......@@ -40,8 +40,8 @@ type Result struct {
err error
visitor Visitor
sources []Visitor
singular bool
sources []Visitor
singleItemImplied bool
ignoreErrors []utilerrors.Matcher
......@@ -82,10 +82,10 @@ func (r *Result) Visit(fn VisitorFunc) error {
return utilerrors.FilterOut(err, r.ignoreErrors...)
}
// IntoSingular sets the provided boolean pointer to true if the Builder input
// reflected a single item, or multiple.
func (r *Result) IntoSingular(b *bool) *Result {
*b = r.singular
// IntoSingleItemImplied sets the provided boolean pointer to true if the Builder input
// implies a single item, or multiple.
func (r *Result) IntoSingleItemImplied(b *bool) *Result {
*b = r.singleItemImplied
return r
}
......@@ -136,7 +136,7 @@ func (r *Result) Object() (runtime.Object, error) {
}
if len(objects) == 1 {
if r.singular {
if r.singleItemImplied {
return objects[0], nil
}
// if the item is a list already, don't create another list
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment