Skip to content

[deckhouse-cli] Push with editions, docs, queue http draft - #500

Merged
ldmonster merged 7 commits into
mainfrom
feat/push-with-editions
Oct 2, 2026
Merged

ldmonster merged 7 commits into
mainfrom
feat/push-with-editions

Conversation

@ldmonster

@ldmonster ldmonster commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Description

This PR bundles four changes:

  1. d8 mirror push to an edition repo puts the edition-independent installer and d8 CLI one level above the edition, where the Deckhouse registry keeps them. cse becomes a recognized edition.
  2. d8 system queue list|main --http reads the queues through the API server (pods/portforward) instead of exec'ing curl in the Deckhouse pod.
  3. d8 system api (hidden) is a client for the runtime API of the Deckhouse controller (/api/v1, Module v2), with typed structures for every route.
  4. Docs: the four specs under docs/ are rewritten in one strict format, and internal/system/README.md documents the new flag and command.

1. Mirror push to an edition repo

When the push target ends with an edition (ce, be, se, se-plus, ee, fe, cse), installer/ and deckhouse-cli/ (the binary, its plugins and the plugins index) go to the root above it, and everything else stays under the target. This is the default, there is no flag. A path that is only an edition (registry.example.com/ee) is not split: it names a project called ee, the same rule the registry-packages-proxy applies.

Bundle layout Pushed to, for target registry.example.com/deckhouse/ee
platform, modules, packages, security registry.example.com/deckhouse/ee/... (unchanged)
installer/ registry.example.com/deckhouse/installer
deckhouse-cli/, deckhouse-cli/plugins/<name> registry.example.com/deckhouse/deckhouse-cli, .../deckhouse-cli/plugins/<name>
  • The split lives in SplitTargetEdition and PushService.clientFor (internal/mirror/push.go). Push announces it with Target is the EE edition repo: installer and deckhouse-cli are pushed outside it, to registry.example.com/deckhouse.
  • pkg.CSEEdition joins pkg.Edition, so d8 mirror pull from .../deckhouse/cse also reads the installer and d8 CLI from the root above it and reports the edition as CSE.
  • --include-module help now says that release-channel versions are added on top of a constraint range without being checked against it, so an upper bound does not apply to them. A new test case pins that behaviour.

2. d8 system queue list|main --http

  • With --http, d8 opens the leader pod's pods/portforward (WebSockets with an SPDY fallback, like kubectl) and sends GET /queue/list.<format> to the controller's debug server on 127.0.0.1:9652 itself. Nothing runs in the container. Without the flag the exec path is unchanged.
  • pods/proxy cannot do this: the debug server listens on loopback only, and the kube-rbac-proxy that publishes it on :4204 never receives the user's token, because the API server drops the Authorization header before proxying.
  • --watch keeps one tunnel open and redials, looking up the leader again, after a failed refresh. Ctrl+C now also cancels a fetch in flight, on the exec path too.
  • If the kubelet cannot reach the port, the reason from the port-forward error stream becomes the error message instead of a bare EOF.
  • The flag needs create pods/portforward in d8-system instead of create pods/exec; with user-authz that is the portForwarding switch.

3. d8 system api (hidden)

One leaf per route of the controller's runtime API (deckhouse-controller/internal/packages/api), which exists only with DECKHOUSE_ENABLE_MODULE_V2=true, hence the hidden command:

Command Route
healthz, readyz, endpoints, metrics /healthz, /readyz, /endpoints, /metrics
pprof <name> [--seconds] [--debug] /debug/pprof/<name>
queues dump [--name], scheduler dump [--name], requirements dump /api/v1/{queues,scheduler,requirements}/dump
packages dump [--name], packages global dump, packages render <name>, packages snapshots <name> /api/v1/packages/...
get <path> any route, as is
  • HTTP only. Every command is a GET through the pods/proxy subresource of the leader pod to the controller's HTTP listener, which binds the pod IP on port self (4222); a port-forward dials localhost and cannot reach it. The transport sends the request through rest.HTTPClientFor, because rest.Request drops the body and status of the controller's plain-text error answers. An error of the API server itself, such as a refused permission, still comes back as a Kubernetes error. --pod queries a replica other than the leader.
  • Packages. The controller of deckhouse main at 8010976436 does not serve /api/v1/packages over HTTP yet, so there the packages commands answer 404.
  • Output. Dumps print -o yaml (the default) or json exactly as the controller encodes them. -o text gives summary tables for queues, the scheduler and packages.
  • Errors. The client reports failures with sentinel errors for errors.Is (apiclient/errors.go). A non-2xx answer is a StatusError that unwraps to ErrNotFound (404), ErrBadRequest (400) or ErrServerError (5xx). A dump that answers null for a name is ErrUnknownPackage, and the global module before initialization is ErrGlobalNotLoaded. ErrNotReady, ErrNoLeader and ErrNoSelfPort cover readiness and the pod lookup.
  • Types. internal/system/cmd/api/apiclient/types.go mirrors every answer at deckhouse main 8010976436: queues, scheduler nodes and decisions, requirements, applications and modules with status, conditions, the nelm progress report, definitions, licensing and repository, the global module, and hook snapshots with informer counters. The client has one typed method per route.

4. Docs

  • docs/mirror-bundle-layout.md, docs/mirror-pull-dry-run.md, docs/plugins.md and docs/self-update.md share one format now: scope and an "as of" date, RFC 2119 roles, a reference to the implementing code for every rule, troubleshooting, and a final "Known deviations" section that lists behaviour, verified by running d8, that breaks the contract (13, 9, 19 and 18 items). The deviations are documented, not fixed, in this PR.
  • docs/mirror-bundle-layout.md §2.9 describes the edition split. internal/system/README.md documents --http, the new access path and d8 system api.

Why do we need it, and what problem does it solve?

  • Edition push. d8 mirror pull reads the installer and deckhouse-cli from the root above the edition repo, and the in-cluster registry-packages-proxy looks for deckhouse-cli there too, but push wrote them under the edition (.../deckhouse/ee/installer). A later pull from such a mirror skipped them, and the cluster's proxy could not find deckhouse-cli and its plugins.
  • queue --http. The exec path needs pods/exec and curl in the image, prints an HTTP error body as if it were the dump (curl runs without --fail and its stderr is dropped), and starts a new exec every second in --watch. The HTTP path needs neither, reports HTTP and kubelet errors, and reuses one connection.
  • d8 system api. Module v2 gave the controller a new HTTP API (/api/v1), and d8 had no client for it. This command covers every route and fixes the wire format in typed structures that other commands, such as d8 status, can reuse.
  • Docs. The documents now state the contract with references to the code, so a change that breaks a rule is visible, and the known-deviation lists give the next fixes a checklist.

What is the expected result?

$ d8 s queue list --http
Summary:
- 'main' queue: empty.
- 127 other queues (0 active, 127 empty): 0 tasks.
- no tasks to handle.

$ d8 s queue main --http -o json
{"name":"main","tasksCount":0,"status":"waiting for task 1m48s"}

$ d8 s api readyz
Startup converge done.

d8 s api queues dump -o text against the test fixtures:

Queue 'prometheus': 2 task(s)
  1. Run (enqueued 1.5s, next retry -500ms)
  2. HookRun:hooks/a.go (enqueued 300ms, next retry 2s)
     error: hook failed: exit status 1
Summary: 3 queue(s), 1 active, 2 task(s).

Checklist

  • The code is covered by unit tests.
  • e2e tests passed. (There is no e2e suite for these commands; queue --http and the api transports were checked by hand on a cluster, see Verified.)
  • Documentation updated according to the changes.
  • Changes were tested in the Kubernetes cluster manually. (queue --http and the api transports; the edition push is covered by unit tests.)

Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
@ldmonster ldmonster self-assigned this Oct 1, 2026
@ldmonster ldmonster added the enhancement New feature or request label Oct 1, 2026
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
@ldmonster ldmonster changed the title [deckhouse-cli] push with editions [deckhouse-cli] Push with editions, docs, queue http draft Oct 2, 2026
@ldmonster
ldmonster merged commit b518fbe into main Oct 2, 2026
@ldmonster
ldmonster deleted the feat/push-with-editions branch October 2, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant