User guide: kaironctl get --selector
kaironctl delete RESOURCE --selector k=v (see
kaironctl-bulk-delete.md) added label-selector
matching to the write side of kaironctl, but the read side was left
behind: kaironctl get had no way to narrow its listing by label at all.
Previewing exactly which Machines a selector reaches -- before trusting it
to a bulk delete, or just to find every Machine one load test created --
meant piping kaironctl get's full output through grep, or reaching for
kubectl get -l instead. This adds --selector to kaironctl get too, as
the read-side counterpart bulk delete was always missing.
Usage
kaironctl get [RESOURCE] [--selector k=v] [--selector k2=v2 ...] [-n NAMESPACE]
RESOURCE keeps its existing default of machine when omitted (unlike
delete RESOURCE --selector, which requires an explicit kind -- see that
guide's own reasoning for why bulk deletion is the one place a wrong
default would be dangerous; a mere listing carries no such risk).
--selector is optional and repeatable; every key=value pair given must
match (logical AND, never OR), using the exact same model.LabelsMatch
rule as delete --selector, MachineDisruptionBudget.spec.selector, and
MachineNetworkPolicy.spec.selector. Omitting --selector entirely lists
every resource of that kind, exactly as kaironctl get has always behaved.
$ kaironctl get machine
NAME NODE PHASE CPU MEMORY IP
loadtest-1 node-a Running 2 4Gi 10.0.1.5
loadtest-2 node-b Running 2 4Gi 10.0.1.6
web-1 node-a Running 4 8Gi 10.0.1.7
$ kaironctl get machine --selector purpose=loadtest
NAME NODE PHASE CPU MEMORY IP
loadtest-1 node-a Running 2 4Gi 10.0.1.5
loadtest-2 node-b Running 2 4Gi 10.0.1.6
$ kaironctl get machine --selector purpose=loadtest --selector env=staging
NAME NODE PHASE CPU MEMORY IP
That last example matches nothing: get, unlike delete, prints just its
usual header row for an empty result (the same thing it has always printed
for a namespace with no resources of that kind at all) rather than growing
a special-cased "nothing matched" message of its own -- an empty list
reads the same way here regardless of why it's empty.
Every kind kaironctl get already lists gets this for free: machine,
migration, snapshot, restore, quota, budget, machineset,
instancetype, migrationpolicy, snapshotschedule, networkpolicy, and
securitygroup (same aliases as always) -- one generic helper
(selectorFilter in internal/kaironctl/kaironctl.go), applied once per
kind's existing list-and-print case, not a special case bolted onto one
kind.
Why this is a generic helper, not one filter function per kind
kaironctl get's dozen kinds each already have their own List*Namespace
call and their own per-kind print loop (see the file's own long switch in
cmdGet); the only thing all twelve share is that every item's labels live
at .Metadata.Labels. Go's generics can't express "any struct with a
Metadata field" structurally without reflection, so selectorFilter[T any](items []T, selector map[string]string, labels func(T) map[string]string) []T takes a small accessor closure instead -- one line
added per case (items = selectorFilter(items, selector, func(m model.Machine) map[string]string { return m.Metadata.Labels })), reusing
the exact same model.LabelsMatch bulk delete's matchingNames already
calls, rather than either thirteen near-identical filter functions or a
reflection-based one.
Real limits today (first cut)
- Selector matching, not a general query language. Same limit
delete --selectoralready has:model.LabelsMatch's plain equality-per-key semantics only, no set-basedin/notin, no!=. - No
-o wide/-o jsonoutput mode.--selectoronly narrows which rows print, in the exact same tab-separated columnskaironctl getalready printed for that kind; it does not add a new output format. - No new RBAC. This reuses exactly the same per-kind
listcallskaironctl getalready needed with no selector at all --scripts/check_rbac_coverage.pywas re-run to confirm nothing changed.