Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling empty resourcelist #131

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions cmd/get/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package get
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strings"

Expand Down Expand Up @@ -107,7 +108,7 @@ var GetCmd = &cobra.Command{
getClusterScopedResources(resourceNamePlural, resourceGroup, vars.GetArgs[resourceNamePlural+"."+resourceGroup])
}
}
handleOutput()
handleOutput(os.Stdout)
},
}

Expand Down Expand Up @@ -430,27 +431,33 @@ func handleObject(obj unstructured.Unstructured) error {
return nil
}

func handleOutput() {
func handleOutput(w io.Writer) {
printer := cliprint.NewTablePrinter(cliprint.PrintOptions{NoHeaders: vars.NoHeaders, Wide: vars.Wide, WithNamespace: false, ShowLabels: false})
_resources := make([]string, 0, len(vars.GetArgs))
var includesClusterScoped bool
for resource := range vars.GetArgs {
_resources = append(_resources, resource)
// if at least one resource is cluster-scoped, never include a namespace in the output if no resources are found of the kind
_, _, namespaced, _ := kindGroupNamespaced(resource)
if !namespaced {
includesClusterScoped = true
}
}
resources := strings.Join(_resources, ",")
if vars.OutputStringVar == "json" {
if vars.SingleResource && len(vars.UnstructuredList.Items) == 1 {
data, _ := json.MarshalIndent(vars.UnstructuredList.Items[0].Object, "", " ")
data = append(data, '\n')
fmt.Printf("%s", data)
fmt.Fprintf(w, "%s", data)
} else if !vars.SingleResource && len(vars.UnstructuredList.Items) > 0 {
data, _ := json.MarshalIndent(vars.UnstructuredList, "", " ")
data = append(data, '\n')
fmt.Printf("%s", data)
fmt.Fprintf(w, "%s", data)
} else {
if vars.Namespace != "" {
fmt.Printf("No resources %s found in %s namespace.\n", resources, vars.Namespace)
fmt.Fprintf(w, "No resources %s found in %s namespace.\n", resources, vars.Namespace)
} else {
fmt.Printf("No resources %s found.\n", resources)
fmt.Fprintf(w, "No resources %s found.\n", resources)
}
}
} else if strings.HasPrefix(vars.OutputStringVar, "jsonpath=") {
Expand All @@ -461,23 +468,23 @@ func handleOutput() {
helpers.ExecuteJsonPath(vars.JsonPathList, jsonPathTemplate)
} else {
if vars.Namespace != "" {
fmt.Printf("No resources %s found in %s namespace.\n", resources, vars.Namespace)
fmt.Fprintf(w, "No resources %s found in %s namespace.\n", resources, vars.Namespace)
} else {
fmt.Printf("No resources %s found.\n", resources)
fmt.Fprintf(w, "No resources %s found.\n", resources)
}
}
} else if vars.OutputStringVar == "yaml" {
if vars.SingleResource && len(vars.UnstructuredList.Items) == 1 {
data, _ := yaml.Marshal(vars.UnstructuredList.Items[0].Object)
fmt.Printf("%s", data)
fmt.Fprintf(w, "%s", data)
} else if len(vars.UnstructuredList.Items) > 0 {
data, _ := yaml.Marshal(vars.UnstructuredList)
fmt.Printf("%s", data)
fmt.Fprintf(w, "%s", data)
} else {
if vars.Namespace != "" {
fmt.Printf("No resources %s found in %s namespace.\n", resources, vars.Namespace)
fmt.Fprintf(w, "No resources %s found in %s namespace.\n", resources, vars.Namespace)
} else {
fmt.Printf("No resources %s found.\n", resources)
fmt.Fprintf(w, "No resources %s found.\n", resources)
}
}
} else {
Expand All @@ -489,13 +496,14 @@ func handleOutput() {
vars.Table = metav1.Table{}
}
if vars.Output.Len() == 0 {
if vars.Namespace != "" {
fmt.Printf("No resources %s found in %s namespace.\n", resources, vars.Namespace)
// never print the (default/current) namespace if at least one cluster-scoped resource is requested
if vars.Namespace == "" || includesClusterScoped {
fmt.Fprintf(w, "No resources %s found.\n", resources)
} else {
fmt.Printf("No resources %s found.\n", resources)
fmt.Fprintf(w, "No resources %s found in %s namespace.\n", resources, vars.Namespace)
}
} else {
vars.Output.WriteTo(os.Stdout)
vars.Output.WriteTo(w)
}
}
}
71 changes: 71 additions & 0 deletions cmd/get/get_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package get

import (
"bytes"
"strings"
"testing"

"github.com/gmeghnag/omc/types"
"github.com/gmeghnag/omc/vars"
)

func TestHandleEmptyWideOutput(t *testing.T) {
tests := []struct {
name string
namespace string
rtype []string
resources *types.UnstructuredList
want string
}{
{
name: "single cluster scoped crd all namespaces",
namespace: "",
rtype: []string{"fakeclusterscopedresources.operator.openshift.io"},
want: "No resources fakeclusterscopedresources.operator.openshift.io found.\n",
},
{
name: "single cluster scoped crd default namespace",
namespace: "default",
rtype: []string{"fakeclusterscopedresources.operator.openshift.io"},
want: "No resources fakeclusterscopedresources.operator.openshift.io found.\n",
},
{
name: "single namespaced scoped crd all namespaces",
namespace: "",
rtype: []string{"fakenamespacescopedresources.operator.openshift.io"},
want: "No resources fakenamespacescopedresources.operator.openshift.io found.\n",
},
{
name: "single namespaced scoped crd default namespace",
namespace: "default",
rtype: []string{"fakenamespacescopedresources.operator.openshift.io"},
want: "No resources fakenamespacescopedresources.operator.openshift.io found in default namespace.\n",
},
{
name: "cluster and namespaced scoped crd all namespaces",
namespace: "",
rtype: []string{"fakeclusterscopedresources.operator.openshift.io,fakenamespacescopedresources.operator.openshift.io"},
want: "No resources fakeclusterscopedresources.operator.openshift.io,fakenamespacescopedresources.operator.openshift.io found.\n",
},
{
name: "cluster and namespaced scoped crd default namespace",
namespace: "default",
rtype: []string{"fakeclusterscopedresources.operator.openshift.io,fakenamespacescopedresources.operator.openshift.io"},
want: "No resources fakeclusterscopedresources.operator.openshift.io,fakenamespacescopedresources.operator.openshift.io found.\n",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var output bytes.Buffer
vars.MustGatherRootPath = "../../testdata/"
vars.Namespace = tt.namespace
validateArgs(tt.rtype)
handleOutput(&output)
if !strings.Contains(output.String(), tt.want) {
t.Errorf("Got: %v \n", output.String())
t.Errorf("Want: %v \n", tt.want)
}
vars.GetArgs = make(map[string]map[string]struct{})
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: fakeclusterscopedresource.operator.openshift.io
spec:
conversion:
strategy: None
group: operator.openshift.io
names:
kind: FakeClusterScopedResource
listKind: FakeClusterScopedResourceList
plural: fakeclusterscopedresources
singular: fakeclusterscopedresource
scope: Cluster
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: "FakeClusterScopedResource is a fake cluster-scoped resource for testing."
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: spec holds user settable values for configuration
properties:
fakeProperty:
description: "fakeProperty for testing"
type: object
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: fakenamespacescopedresource.operator.openshift.io
spec:
conversion:
strategy: None
group: operator.openshift.io
names:
kind: FakenamespacescopedResource
listKind: FakenamespacescopedResourceList
plural: fakenamespacescopedresources
singular: fakenamespacescopedresource
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: "FakenamespacescopedResource is a fake cluster-scoped resource for testing."
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: spec holds user settable values for configuration
properties:
fakeProperty:
description: "fakeProperty for testing"
type: object
type: object
required:
- spec
type: object
served: true
storage: true
subresources:
status: {}