[deckhouse-cli] add --scope flag to d8 k for cross-namespace listing#399
Merged
Conversation
4 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
This PR extends the d8 k get (kubectl wrapper) command with a --scope flag that enables Deckhouse server-side ACL-filtering for cross-namespace listings by injecting X-Deckhouse-Scope / X-Deckhouse-Project headers into every Kubernetes API request (via rest.Config.WrapTransport). It also adds UX improvements: a targeted hint when get -A fails with a cluster-scope Forbidden, and shell completion that can enumerate Deckhouse Project names.
Changes:
- Add
--scope=accessible|projects|system|project:<name>tod8 k get, validate it early, and inject corresponding headers via a composedWrapTransport. - Append an actionable
--scopehint to kubectl’s cluster-scope Forbidden fatal path (and ensured8 <subcmd>messages are rewritten tod8 k <subcmd>there too). - Add tests covering scope parsing, usage validation, hint generation, and WrapTransport composition behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
cmd/commands/kubectl.go |
Implements parsing/validation, header injection transport wrapper, hinting on Forbidden, and shell completion for --scope. |
cmd/commands/kubectl_scope_test.go |
Adds unit tests for parsing, usage guard, Forbidden hint behavior, and WrapTransport composition/injection. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Glitchy-Sheep
requested changes
Jul 13, 2026
Adds --scope=accessible|projects|system|project:<name> to `d8 k get`, wired via ConfigFlags.WithWrapConfigFn: parses and validates the flag in PersistentPreRunE (so a bad value fails fast, before any request), then injects the corresponding X-Deckhouse-Scope / X-Deckhouse-Project headers into every outgoing request via a rest.Config.WrapTransport RoundTripper. Composes with any pre-existing WrapTransport (e.g. an exec-credential auth plugin) rather than replacing it -- covered by a dedicated test, since this is a new pattern for this codebase. --scope is a flag on `get` only, deliberately NOT persistent on the `k` root: the server only ACL-filters list/get/watch of top-level resources, which in kubectl terms is `get` (and `get -w`). On other subcommands the header is either ignored (top hits metrics.k8s.io; delete/patch are not bypassed; logs/exec are subresources) or actively misleading (describe's internal LIST would be silently filtered), so the flag is not offered there and also becomes discoverable in `d8 k get --help`. --scope requires -A/--all-namespaces and is refused alongside an explicit -n/--namespace: it filters a cross-namespace listing, which needs the cluster-scoped request URL that -A produces. Without -A the request is locked to a single namespace and --scope could only ever narrow that one namespace, silently turning a clean 403 into a misleading empty result; the guard reports the -n conflict in preference to the missing -A. When a `get -A` WITHOUT --scope dies with kubectl's cluster-scope Forbidden error, a hint is appended pointing at --scope, with copy-pasteable examples for each scope value built from the resource the user actually asked for (placeholder when unknown -- never a guess). This hooks cmdutil.BehaviorOnFatal, which as a bonus routes fatal messages through the same "d8 <subcmd>" -> "d8 k <subcmd>" rewrite the rest of stderr gets -- kubectl's fatal path previously bypassed the rewriter entirely. Registers shell completion for --scope: the static enum values plus project:<name> for each real project. Project names are listed via the discovery RESTMapper (group deckhouse.io, Kind Project) rather than a hardcoded GVR, so completion keeps working across served-version bumps. Completing a non-project value never triggers a cluster round-trip. Requires server-side support from the generalized namespace ACL-filtering kube-apiserver patch (deckhouse/deckhouse); with no --scope passed and no cluster-scope Forbidden on a scope-eligible `get -A`, behavior is unchanged. Signed-off-by: Aleksandr Sinichkin <aleksandr.sinichkin@flant.com>
Glitchy-Sheep
approved these changes
Jul 13, 2026
ldmonster
approved these changes
Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds --scope=accessible|projects|system|project: to
d8 k get, wired via ConfigFlags.WithWrapConfigFn: parses and validates the flag in PersistentPreRunE (so a bad value fails fast, before any request), then injects the corresponding X-Deckhouse-Scope / X-Deckhouse-Project headers into every outgoing request via a rest.Config.WrapTransport RoundTripper. Composes with any pre-existing WrapTransport (e.g. an exec-credential auth plugin) rather than replacing it -- covered by a dedicated test, since this is a new pattern for this codebase.--scope is a flag on
getonly, deliberately NOT persistent on thekroot: the server only ACL-filters list/get/watch of top-level resources, which in kubectl terms isget(andget -w). On other subcommands the header is either ignored (top hits metrics.k8s.io; delete/patch are not bypassed; logs/exec are subresources) or actively misleading (describe's internal LIST would be silently filtered), so the flag is not offered there and also becomes discoverable ind8 k get --help.--scope requires -A/--all-namespaces and is refused alongside an explicit -n/--namespace: it filters a cross-namespace listing, which needs the cluster-scoped request URL that -A produces. Without -A the request is locked to a single namespace and --scope could only ever narrow that one namespace, silently turning a clean 403 into a misleading empty result; the guard reports the -n conflict in preference to the missing -A.
When a
get -AWITHOUT --scope dies with kubectl's cluster-scope Forbidden error, a hint is appended pointing at --scope, with copy-pasteable examples for each scope value built from the resource the user actually asked for (placeholder when unknown -- never a guess). This hooks cmdutil.BehaviorOnFatal, which as a bonus routes fatal messages through the same "d8 " -> "d8 k " rewrite the rest of stderr gets -- kubectl's fatal path previously bypassed the rewriter entirely.Registers shell completion for --scope: the static enum values plus project: for each real project. Project names are listed via the discovery RESTMapper (group deckhouse.io, Kind Project) rather than a hardcoded GVR, so completion keeps working across served-version bumps. Completing a non-project value never triggers a cluster round-trip.
Requires server-side support from the generalized namespace ACL-filtering kube-apiserver patch (deckhouse/deckhouse); with no --scope passed and no cluster-scope Forbidden on a scope-eligible
get -A, behavior is unchanged.