Repository: corneliusweig/ketall (go)

Issue:
maybe exclude event.events.k8s.io by default?
I can hardly think anyone would actually want to get all events (and similar status objects). The output is much more useful when I run `get-all | grep -v ^event`.

Maybe exclude `events/*` and `event.events.k8s.io/*` by default and offer an `--include-events` flag?

Pull request description:
Do not show events by default
This PR introduces a new flag `--exclude` which defaults to `events`, so that no events are shown any longer. It accepts resource names (only plural form) or shortnames. For example `componentstatuses` or `cm` (`configMap`).
The setting can also be configured in the ketall config file.

Closes #4

Patch:
diff --git a/.travis.yml b/.travis.yml
index ac2f983..02c71b5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,8 +15,8 @@ script:
   - make dev
   - make coverage
 
-#after_success:
-#  - bash <(curl -s https://codecov.io/bash)
+after_success:
+  - bash <(curl -s https://codecov.io/bash)
 
 before_deploy:
   - make deploy
diff --git a/cmd/root.go b/cmd/root.go
index f90a7c0..88282d0 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -42,16 +42,23 @@ Like 'kubectl get all', but get _really_ all resources
 Ketall retrieves all resources which allow to be fetched. This complements the
 usual "kubectl get all" command, which excludes all cluster-level and some
 namespaced resources.
+
+More on https://github.com/corneliusweig/ketall/blob/v0.0.2/doc/USAGE.md
 `
 	ketallExamples = `
-  Get all resources
+  Get all resources, excluding events
   $ ketall
 
+  Get all resources, including events
+  $ ketall --exclude=
+
   Get all cluster level resources
   $ ketall --only-scope=cluster
 
   Get all resources in a particular namespace
   $ ketall --only-scope=namespace --namespace=<some>
+
+  Some options can also be configured in the config file 'ketall'
 `
 )
 
@@ -80,6 +87,7 @@ func init() {
 
 	rootCmd.Flags().BoolVar(&ketallOptions.UseCache, constants.FlagUseCache, false, "use cached list of server resources")
 	rootCmd.Flags().StringVar(&ketallOptions.Scope, constants.FlagScope, "", "only resources with scope cluster|namespace")
+	rootCmd.Flags().StringSliceVar(&ketallOptions.Exclusions, constants.FlagExclude, []string{"events"}, "filter by resource name (plural form or short name)")
 
 	ketallOptions.GenericCliFlags.AddFlags(rootCmd.Flags())
 	ketallOptions.PrintFlags.AddFlags(rootCmd)
diff --git a/doc/USAGE.md b/doc/USAGE.md
index 02b3d93..1265afe 100644
--- a/doc/USAGE.md
+++ b/doc/USAGE.md
@@ -64,6 +64,10 @@ The following settings can be configured:
 only-scope: cluster
 namespace: default
 use-cache: true
+# only plural form or abbreviations
+exclude:
+- componentstatuses
+- cm   # configmaps
 ```
 
 ## Installation
diff --git a/pkg/client/client.go b/pkg/client/client.go
index 8b87e87..ce53348 100644
--- a/pkg/client/client.go
+++ b/pkg/client/client.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"github.com/corneliusweig/ketall/pkg/constants"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
@@ -42,11 +43,13 @@ func GetAllServerResources(flags *genericclioptions.ConfigFlags) (runtime.Object
 	useCache := viper.GetBool(constants.FlagUseCache)
 	scope := viper.GetString(constants.FlagScope)
 
-	resNames, err := FetchAvailableResourceNames(useCache, scope, flags)
+	grs, err := FetchAvailableGroupResources(useCache, scope, flags)
 	if err != nil {
-		return nil, errors.Wrap(err, "fetch available resources")
+		return nil, errors.Wrap(err, "fetch available group resources")
 	}
 
+	resNames := extractRelevantResourceNames(grs, viper.GetStringSlice(constants.FlagExclude))
+
 	request := resource.NewBuilder(flags).
 		Unstructured().
 		SelectAllParam(true).
@@ -70,7 +73,7 @@ func GetAllServerResources(flags *genericclioptions.ConfigFlags) (runtime.Object
 	return response.Object()
 }
 
-func FetchAvailableResourceNames(cache bool, scope string, flags *genericclioptions.ConfigFlags) ([]string, error) {
+func FetchAvailableGroupResources(cache bool, scope string, flags *genericclioptions.ConfigFlags) ([]groupResource, error) {
 	client, err := flags.ToDiscoveryClient()
 	if err != nil {
 		return nil, errors.Wrap(err, "discovery client")
@@ -120,13 +123,27 @@ func FetchAvailableResourceNames(cache bool, scope string, flags *genericcliopti
 		}
 	}
 
+	return grs, nil
+}
+
+func extractRelevantResourceNames(grs []groupResource, exclusions []string) []string {
 	sort.Stable(sortableGroupResource(grs))
+	forbidden := sets.NewString(exclusions...)
+
 	result := []string{}
 	for _, r := range grs {
-		result = append(result, r.fullName())
+		name := r.fullName()
+		resourceIds := r.APIResource.ShortNames
+		resourceIds = append(resourceIds, r.APIResource.Name)
+		if forbidden.HasAny(resourceIds...) {
+			logrus.Debugf("Excluding %s", name)
+			continue
+		}
+		result = append(result, name)
 	}
 
-	return result, nil
+	logrus.Debugf("Resources to fetch: %s", result)
+	return result
 }
 
 func getResourceScope(scope string) (skipCluster, skipNamespace bool, err error) {
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index 6fafc1b..efb0ce3 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -23,5 +23,6 @@ const (
 
 	FlagUseCache  = "use-cache"
 	FlagScope     = "only-scope"
+	FlagExclude   = "exclude"
 	FlagNamespace = "namespace"
 )
diff --git a/pkg/options/options.go b/pkg/options/options.go
index 49f2c68..b3ac3c7 100644
--- a/pkg/options/options.go
+++ b/pkg/options/options.go
@@ -31,6 +31,7 @@ type KetallOptions struct {
 	PrintFlags      KAPrintFlags
 	UseCache        bool
 	Scope           string
+	Exclusions      []string
 	Streams         *genericclioptions.IOStreams
 }
 


Test patch:
diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go
new file mode 100644
index 0000000..5ac42da
--- /dev/null
+++ b/pkg/client/client_test.go
@@ -0,0 +1,81 @@
+/*
+Copyright 2019 Cornelius Weig
+
+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 client
+
+import (
+	"github.com/stretchr/testify/assert"
+	"k8s.io/apimachinery/pkg/apis/meta/v1"
+	"testing"
+)
+
+func TestExtractRelevantResourceNames(t *testing.T) {
+	var tests = []struct {
+		testName  string
+		resources []v1.APIResource
+		groups    []string
+		exclude   []string
+		expected  []string
+	}{
+		{
+			testName:  "a single resource",
+			resources: []v1.APIResource{{Name: "foo"}},
+			groups:    []string{"group"},
+			expected:  []string{"foo.group"},
+		},
+		{
+			testName:  "two resources, without group",
+			resources: []v1.APIResource{{Name: "foo"}, {Name: "bar"}},
+			groups:    []string{"group", ""},
+			expected:  []string{"bar", "foo.group"},
+		},
+		{
+			testName:  "two resources, same group",
+			resources: []v1.APIResource{{Name: "foo"}, {Name: "bar"}},
+			groups:    []string{"group", "group"},
+			expected:  []string{"bar.group", "foo.group"},
+		},
+		{
+			testName:  "two filtered by Name",
+			resources: []v1.APIResource{{Name: "foo"}, {Name: "bar"}},
+			groups:    []string{"group", "puorg"},
+			exclude:   []string{"bar"},
+			expected:  []string{"foo.group"},
+		},
+		{
+			testName:  "two filtered by ShortName",
+			resources: []v1.APIResource{{Name: "foo", ShortNames: []string{"baz"}}, {Name: "bar"}},
+			groups:    []string{"group", "puorg"},
+			exclude:   []string{"baz"},
+			expected:  []string{"bar.puorg"},
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.testName, func(t *testing.T) {
+			var grs []groupResource
+			for i := range test.resources {
+				grs = append(grs, groupResource{
+					APIGroup:    test.groups[i],
+					APIResource: test.resources[i],
+				})
+			}
+
+			names := extractRelevantResourceNames(grs, test.exclude)
+			assert.Equal(t, test.expected, names)
+		})
+	}
+}