Skip to content

Commit 99b0002

Browse files
committed
Implement label selector for lists
Fixes #68.
1 parent ef3162d commit 99b0002

File tree

3 files changed

+28
-9
lines changed

3 files changed

+28
-9
lines changed

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
A kubectl plugin to explore ownership relationships between Kubernetes objects
1010
through `ownersReferences` on the objects.
1111

12-
The [`kubectl lineage`](https://github.com/tohjustin/kube-lineage) plugin is very similar to `kubectl tree`, but it
12+
The [`kubectl lineage`](https://github.com/tohjustin/kube-lineage) plugin is very similar to `kubectl tree`, but it
1313
[understands](https://github.com/tohjustin/kube-lineage#supported-relationships)
1414
logical relationships between some API objects without needing ownerReferences.
1515

@@ -39,8 +39,16 @@ Example (Agones Fleet):
3939
By default, the plugin will only search "namespaced" objects in the same
4040
namespace as the specified object.
4141

42-
You can use the `-A` or `--all-namespaces` flag to search namespaced and
43-
non-namespaced objects in all namespaces.
42+
- `-A`, `--all-namespaces`: Search namespaced and non-namespaced objects in all namespaces.
43+
44+
- `-l`, `--selector`: Selector (label query) to filter on. Supports equality (`=`, `==`, `!=`), set-based (`in`, `notin`), and existence operators. Examples:
45+
- `-l key1=value1,key2=value2` (equality)
46+
- `-l "env in (prod,staging)"` (set-based)
47+
- `-l "tier=frontend,env!=test"` (mixed)
48+
49+
This helps reduce workload and data volume when working with large clusters.
50+
51+
- `--condition-types`: Comma-separated list of condition types to check (default: Ready). Example: `Ready,Processed,Scheduled`.
4452

4553
## Author
4654

cmd/kubectl-tree/query.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
)
1616

1717
// getAllResources finds all API objects in specified API resources in all namespaces (or non-namespaced).
18-
func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) ([]unstructured.Unstructured, error) {
18+
func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool, labelSelector string) ([]unstructured.Unstructured, error) {
1919
var mu sync.Mutex
2020
var wg sync.WaitGroup
2121
var out []unstructured.Unstructured
@@ -33,7 +33,7 @@ func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) (
3333
go func(a apiResource) {
3434
defer wg.Done()
3535
klog.V(4).Infof("[query api] start: %s", a.GroupVersionResource())
36-
v, err := queryAPI(client, a, allNs)
36+
v, err := queryAPI(client, a, allNs, labelSelector)
3737
if err != nil {
3838
if errors.IsForbidden(err) {
3939
// should not fail the overall process, but print an info message indicating the permission issue
@@ -59,7 +59,7 @@ func getAllResources(client dynamic.Interface, apis []apiResource, allNs bool) (
5959
return out, errResult
6060
}
6161

62-
func queryAPI(client dynamic.Interface, api apiResource, allNs bool) ([]unstructured.Unstructured, error) {
62+
func queryAPI(client dynamic.Interface, api apiResource, allNs bool, labelSelector string) ([]unstructured.Unstructured, error) {
6363
var out []unstructured.Unstructured
6464

6565
var next string
@@ -76,10 +76,14 @@ func queryAPI(client dynamic.Interface, api apiResource, allNs bool) ([]unstruct
7676
} else {
7777
intf = nintf
7878
}
79-
resp, err := intf.List(context.TODO(), metav1.ListOptions{
79+
listOptions := metav1.ListOptions{
8080
Limit: 250,
8181
Continue: next,
82-
})
82+
}
83+
if labelSelector != "" {
84+
listOptions.LabelSelector = labelSelector
85+
}
86+
resp, err := intf.List(context.TODO(), listOptions)
8387
if err != nil {
8488
return nil, fmt.Errorf("listing resources failed (%s): %w", api.GroupVersionResource(), err)
8589
}

cmd/kubectl-tree/rootcmd.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const (
4040
allNamespacesFlag = "all-namespaces"
4141
colorFlag = "color"
4242
conditionTypesFlag = "condition-types"
43+
selectorFlag = "selector"
4344
)
4445

4546
var (
@@ -95,6 +96,11 @@ func run(command *cobra.Command, args []string) error {
9596
return err
9697
}
9798

99+
labelSelector, err := command.Flags().GetString(selectorFlag)
100+
if err != nil {
101+
return err
102+
}
103+
98104
restConfig, err := cf.ToRESTConfig()
99105
if err != nil {
100106
return err
@@ -175,7 +181,7 @@ func run(command *cobra.Command, args []string) error {
175181
klog.V(5).Infof("target parent object: %#v", obj)
176182

177183
klog.V(2).Infof("querying all api objects")
178-
apiObjects, err := getAllResources(dyn, apis.resources(), allNs)
184+
apiObjects, err := getAllResources(dyn, apis.resources(), allNs, labelSelector)
179185
if err != nil {
180186
return fmt.Errorf("error while querying api objects: %w", err)
181187
}
@@ -207,6 +213,7 @@ func init() {
207213
rootCmd.Flags().BoolP(allNamespacesFlag, "A", false, "query all objects in all API groups, both namespaced and non-namespaced")
208214
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.")
209215
rootCmd.Flags().StringSlice(conditionTypesFlag, []string{"Ready"}, "Comma-separated list of condition types to check (default: Ready). Example: Ready,Processed,Scheduled")
216+
rootCmd.Flags().StringP(selectorFlag, "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='. (e.g. -l key1=value1,key2=value2)")
210217

211218
cf.AddFlags(rootCmd.Flags())
212219
if err := flag.Set("logtostderr", "true"); err != nil {

0 commit comments

Comments
 (0)