From 4cd544c31c345764371f1aac1dc94cedc423e51f Mon Sep 17 00:00:00 2001 From: Carlos Sanchez Date: Sun, 5 Oct 2025 11:20:11 +0200 Subject: [PATCH] fix: do not fail if missing permissions to some CRDs Continue and display the tree with the objects that we can access --- cmd/kubectl-tree/query.go | 10 ++++++++-- cmd/kubectl-tree/rootcmd.go | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/kubectl-tree/query.go b/cmd/kubectl-tree/query.go index 3e9517b..1818529 100644 --- a/cmd/kubectl-tree/query.go +++ b/cmd/kubectl-tree/query.go @@ -15,7 +15,7 @@ import ( ) // getAllResources finds all API objects in specified API resources in all namespaces (or non-namespaced). -func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) ([]unstructured.Unstructured, error) { +func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool, ignoreErrors bool) ([]unstructured.Unstructured, error) { var mu sync.Mutex var wg sync.WaitGroup var out []unstructured.Unstructured @@ -38,7 +38,9 @@ func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) ( if errors.IsForbidden(err) { // should not fail the overall process, but print an info message indicating the permission issue klog.V(4).Infof("[query api] skipping forbidden resource: %s", a.GroupVersionResource()) - klog.Infof("cannot query %s (forbidden), omitting from the tree", a.GroupVersionResource().GroupResource()) + if !ignoreErrors { + klog.Infof("cannot query %s (forbidden), omitting from the tree", a.GroupVersionResource().GroupResource()) + } } else { klog.V(4).Infof("[query api] error querying: %s, error=%v", a.GroupVersionResource(), err) errResult = stderrors.Join(errResult, fmt.Errorf("failed to query the %s resources: %w", a.GroupVersionResource(), err)) @@ -56,6 +58,10 @@ func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) ( wg.Wait() klog.V(2).Infof("all goroutines have returned in %v", time.Since(start)) klog.V(2).Infof("query result: error=%v, objects=%d", errResult, len(out)) + if ignoreErrors { + klog.V(2).Infof("ignoring errors, returning all objects") + return out, nil + } return out, errResult } diff --git a/cmd/kubectl-tree/rootcmd.go b/cmd/kubectl-tree/rootcmd.go index 4a29586..b39ea68 100644 --- a/cmd/kubectl-tree/rootcmd.go +++ b/cmd/kubectl-tree/rootcmd.go @@ -37,6 +37,7 @@ import ( const ( allNamespacesFlag = "all-namespaces" colorFlag = "color" + ignoreErrorsFlag = "ignore-errors" ) var cf *genericclioptions.ConfigFlags @@ -72,6 +73,11 @@ func run(command *cobra.Command, args []string) error { allNs = false } + ignoreErrors, err := command.Flags().GetBool(ignoreErrorsFlag) + if err != nil { + ignoreErrors = false + } + colorArg, err := command.Flags().GetString(colorFlag) if err != nil { return err @@ -149,9 +155,9 @@ func run(command *cobra.Command, args []string) error { klog.V(5).Infof("target parent object: %#v", obj) klog.V(2).Infof("querying all api objects") - apiObjects, err := getAllResources(dyn, apis.resources(), allNs) + apiObjects, err := getAllResources(dyn, apis.resources(), allNs, ignoreErrors) if err != nil { - return fmt.Errorf("error while querying api objects: %w", err) + return fmt.Errorf("error while querying api objects, use --ignore-errors to continue: %w", err) } klog.V(2).Infof("found total %d api objects", len(apiObjects)) @@ -180,6 +186,7 @@ func init() { rootCmd.Flags().BoolP(allNamespacesFlag, "A", false, "query all objects in all API groups, both namespaced and non-namespaced") rootCmd.Flags().StringP(colorFlag, "c", "auto", "Enable or disable color output. This can be 'always', 'never', or 'auto' (default = use color only if using tty). The flag is overridden by the NO_COLOR env variable if set.") + rootCmd.Flags().Bool(ignoreErrorsFlag, false, "continue on errors while querying API resources") cf.AddFlags(rootCmd.Flags()) if err := flag.Set("logtostderr", "true"); err != nil {