Skip to content

feat: ork proxy — port-forward CLI for Helm-deployed Orkestra; e2e Go client migration#203

Merged
iAlexeze merged 23 commits into
mainfrom
feat/ork-proxy
Jul 7, 2026
Merged

feat: ork proxy — port-forward CLI for Helm-deployed Orkestra; e2e Go client migration#203
iAlexeze merged 23 commits into
mainfrom
feat/ork-proxy

Conversation

@iAlexeze

@iAlexeze iAlexeze commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds ork proxy — a single command that replaces manual kubectl port-forward calls when working with a Helm-deployed Orkestra. Discovers components by the new orkestra.orkspace.io/komponent label, resolves the Runtime leader via the orkestra-konductor Lease, and keeps all forwards alive with automatic reconnection.
  • Migrates three e2e kubectl subprocess calls to native Go client calls: leader Lease read, SelfSubjectAccessReview for auth checks, and SPDY portforward for kubectl.port-forward assertions.
  • Fixes the kind node readiness spinner interleaving kubectl output with the spinner line.
  • Adds kubectl.restart and kubectl.scale E2E DSL subcommands for rollout mutations in test sequences.

Changed files

cmd/cli/proxy.go (new)

ork proxy command. --for flag uses same vocabulary as ork generate bundle --for (runtime/run, gateway/gw, cc/controlcenter). Per-component port overrides (--runtime-port, --cc-port, --gateway-port), --context, --namespace. Global flags shadowed and hidden. Port conflict pre-check before opening any tunnel. Ctrl+C shutdown via signal.NotifyContext.

pkg/proxy/ (new)

  • discover.goFindService by orkestra.orkspace.io/komponent label; ResolvePod from Service selector; ResolveRuntimePod via orkestra-konductor Lease
  • proxy.goRunAll blocks until ctx cancelled; forwardWithReconnect reconnect loop per goroutine; forward SPDY portforward via k8s.io/client-go/tools/portforward; coloured output using pkg/utils helpers; proactive pod watch calls stop() on pod disappearance; probeReady loops on /health before marking Runtime forward live; reconnecting indicator; exits when all targets are not deployed
  • ports.goCheckPort TCP bind check
  • README.md, docs/01-discovery.md, docs/02-forward.md

pkg/types/types.go

KonductorLeaseName = "orkestra-konductor" constant shared by pkg/proxy/discover.go and pkg/konductor/konduct.go.

charts/orkestra/templates/

Added orkestra.orkspace.io/komponent label to metadata (not selectors) on all six files: runtime-deployment.yaml, runtime-service.yaml, controlcenter-deployment.yaml, controlcenter-service.yaml, gateway-deployment.yaml, gateway-service.yaml.

pkg/e2e/runner.go

Runner gains cs kubernetes.Interface and cfg *rest.Config built after ensureCluster via buildClient(). Threaded through to verifyExpectation and runOnFailure. Still-waiting feedback: prints still waiting [Xs]... every 10 s while a step is polling so long waits do not look like hangs.

pkg/e2e/verify.go

  • resolveLeaderHolderCoordinationV1().Leases().Get() replaces kubectl get lease -o jsonpath; workDir param dropped
  • checkKubectlAuthSelfSubjectAccessReview when e.As == "" (matches kubectl auth can-i default behaviour); SubjectAccessReview with User set when --as is specified
  • checkKubectlPortForward — SPDY portforward + net/http; readyChan eliminates the old curl-retry polling race; pod resolved via lease, service selector, or direct name
  • checkKubectlRestartkubectl rollout restart <kind>/<name> + optional rollout status; ready: true default
  • checkKubectlScalekubectl scale <kind>/<name> --replicas=N + optional rollout status; same default

pkg/e2e/validate.go

validateKubectlRestart and validateKubectlScale — kind and name required.

pkg/types/e2e.go

E2EKubectl gains Restart []E2EKubectlRestart and Scale []E2EKubectlScale. Both structs: kind, name, namespace, ready *bool.

pkg/e2e/onfailure.go

Receives cs kubernetes.Interface; resolveLeaderHolder call sites updated. restart and scale are mutations — not emitted in onFailure diagnostics.

pkg/ork/kind.go

waitForNodesReady signature gains onNodeReady func(nodeName string) callback. Output captured via os.Pipe into a goroutine — kubectl output no longer writes to the terminal. Callback updates the spinner with (N/total) progress. Success marked with final count.

pkg/health/health.go

Binds the listener synchronously before launching the goroutine. A port conflict now returns an error from Start() so ork run fails immediately instead of silently continuing with a dead health server.

documentation/reference/cli/proxy.md (new), index.md

CLI reference page for ork proxy. Added to the Tooling section of the index.

documentation/reference/schema/04-e2e/07-kubectl.md

kubectl.restart and kubectl.scale sections with field tables and examples.

examples/security/deletion-protection/e2e.yaml

Cleanup step: apply ConfigMap with deletionProtection.enabled: false + kubectl.restart gateway. The housekeeper reads enabled: false on startup and removes the ValidatingWebhookConfiguration — deleting the VWC directly does not work because the housekeeper recreates it immediately on DELETED events.

Docs + examples

Replaced all bare kubectl port-forward svc/orkestra-* calls with ork proxy across documentation and example READMEs. Gateway-specific pages use ork proxy --for gateway with curl on localhost:8443. Cross-binary example uses ork proxy -n producer-system.

Key design decisions

Label-based discovery. ork proxy finds components by orkestra.orkspace.io/komponent rather than hardcoded service names, keeping discovery decoupled from chart naming conventions. The label uses komponent (Orkestra's internal spelling) to match the rest of the codebase vocabulary.

Leader-pinned Runtime forward. A service port-forward for the Runtime routes to a random replica. ork proxy reads the orkestra-konductor Lease to resolve the leader pod, so the forwarded connection always reaches the replica with authoritative reconciler state — consistent with how ork e2e targets the leader.

SelfSubjectAccessReview not SubjectAccessReview. kubectl auth can-i without --as sends a SelfSubjectAccessReview — the API server infers the caller's identity. SubjectAccessReview with an empty User field is rejected. The migration now matches kubectl's actual behaviour.

restart/scale are mutations, not assertions. They do not appear in onFailure diagnostics. The ready: true default runs rollout status to confirm the workload settled — set ready: false to skip.

Housekeeper-aware deletion-protection cleanup. Deleting the ValidatingWebhookConfiguration directly fails because the housekeeper Watch fires immediately and recreates it. The only clean escape is to update orkestra-katalog ConfigMap with enabled: false and restart the gateway — the housekeeper then calls cleanupValidatingWebhook on startup and never re-registers.

Inspired by kubectl proxy. The command name and concept follow kubectl proxy — a dedicated proxy command for the tool's own components rather than a generic port-forward wrapper.

iAlexeze added 23 commits July 5, 2026 09:15
…ource types

NetworkPolicy, ResourceQuota, LimitRange, ClusterRole, and ClusterRoleBinding
were missing ForEach field wiring, ExpandForEach* functions, name extractors,
GVR entries, and read blocks — so forEach produced no expansion and none of
the resources appeared as tracked children.
isFileMotif only matched ./  ../  and absolute paths. Bare relative paths
like motifs/01-namespaced/motif.yaml were silently treated as OCI refs,
causing ork validate to fail when run from outside the katalog directory.
…ypes

Six motifs (namespaced, rbac, workloads, network, config, storage) imported
into one katalog. Every resource uses forEach, giving a single runnable
fixture for developing and verifying children changes.
ork e2e --report-file <path> writes test results as a GFM table in addition
to stdout. Designed like Go's -coverprofile — the caller decides what to do
with the file. ork-action passes $GITHUB_STEP_SUMMARY to populate the CI
job summary automatically.
Passed and failed cases are rendered in separate tables so the error
column only appears when there are failures. Newlines in error messages
are collapsed to ". " to keep table rows valid GFM.
… detail

Passed cases print first, failed cases follow with the full error message
indented beneath each — multiline want/got errors render on separate lines.
Also fixes a broken link to the e2e-universal guide in faqs/06-testing.md.
spec.onFailure runs once after all expectations when any failed.
expect[].onFailure runs immediately when that specific checkpoint fails,
capturing cluster state at the moment of failure.

Both accept the same kubectl DSL (get, logs, describe, events, exec)
and raw commands. Assertion fields are ignored — output is always printed.

Also bumps Chart.yaml to v0.7.10 and updates schema docs and fixture.
… isolated vs composite runs

06-full-stack/katalog.yaml declares managed-database commented out so the
root composite can import it from 03-cross-crd without a duplicate CRD error.

Step 1 in 06-full-stack/README.md now tells users to uncomment it when
running in isolation. Root README.md adds a Troubleshooting section explaining
the duplicate CRD error and the fix.
…subject namespace

Cluster-scoped CR fixes cross-namespace owner reference GC — Kubernetes
was deleting namespaced children (NetworkPolicy, ResourceQuota, LimitRange)
because their owner reference pointed to a Namespaced CR in a different
namespace.

ClusterRoleBinding subject namespace hardcoded to default — it was
resolving to the role name (developer/reviewer) from the forEach loop,
producing an invalid namespace value.
…ion use linkTitle

sync-docs.sh splits CHANGELOG.md into one page per release under
content/docs/changelog/; _index.md auto-generated as a release index.

sidebar.yaml: Changelog added after Roadmap.
sidebar.html + single.html: display text uses .LinkTitle so changelog
entries show just the version number while full title remains the page heading.

Also: tip admonition indent fixed in 00-kubernetes-basics.md;
roadmap last-updated date bumped to July 2026;
CHANGELOG.md [UNRELEASED] marker removed — v0.7.10 is ready to ship.
Adds `ork proxy` — a single command that replaces manual `kubectl port-forward`
calls when working with a Helm-deployed Orkestra. Discovers components by the
new `orkestra.orkspace.io/komponent` label (added to Service and Deployment
metadata for runtime, control-center, and gateway), resolves the Runtime leader
pod via the `orkestra-konductor` Lease, and keeps all forwards alive with
automatic reconnection on pod replacement.

- pkg/proxy: discover, forward, port-check packages with SPDY portforward
- cmd/cli/proxy.go: --for flag (same vocabulary as generate bundle), --namespace,
  per-component port overrides, --context, Ctrl+C shutdown
- Charts: orkestra.orkspace.io/komponent label on all component Services and
  Deployments (metadata only, not selectors)
- Docs: documentation/reference/cli/proxy.md + index entry
Replaces three kubectl subprocess calls in the e2e runner with native
Go client calls, removing the kubectl install requirement for these
operations:

- resolveLeaderHolder: kubectl get lease → CoordinationV1().Leases().Get()
- checkKubectlAuth: kubectl auth can-i → SelfSubjectAccessReview (no --as)
  or SubjectAccessReview (with --as), matching kubectl's own behaviour
- checkKubectlPortForward: kubectl port-forward + curl → SPDY portforward
  (k8s.io/client-go/tools/portforward) + net/http, using readyChan to
  eliminate the polling race in the old curl-retry loop

Runner gains a kubernetes.Interface and *rest.Config built after cluster
setup and threaded through to all check functions. onfailure.go updated
to receive the client for leaderElection resolution in diagnostics.
waitForNodesReady was piping kubectl output directly to the terminal,
interleaving with the spinner and leaving no success indicator. Now
captures kubectl output via os.Pipe into a goroutine, parses
"condition met" lines to count ready nodes, and calls a per-node
callback that updates the spinner message with (N/total) progress.
The spinner marks success with the final count once all nodes are ready.
…and examples

Replaces all bare kubectl port-forward calls to Orkestra components
(runtime, cc, gateway) with ork proxy in documentation and example
READMEs. Gateway-specific pages use ork proxy --for gateway with
curl on localhost:8443. Cross-binary cross-operator example uses
ork proxy -n producer-system for the non-default namespace.

Also adds global onFailure to charts/orkestra/e2e.yaml with runtime
leader logs (via leaderElection), gateway component logs, describe,
and events for both deployments.

Notes ork proxy was inspired by kubectl proxy in the CLI reference
and changelog.
ListenAndServe ran in a goroutine — a port conflict was only logged
and Start() returned nil, leaving the server silently dead. Now binds
the listener synchronously before launching the goroutine; a conflict
returns an error from Start() so ork run fails immediately.
Watch each forwarded pod independently so reconnection starts on pod
deletion without waiting for traffic to surface the broken tunnel.
Probe /health after SPDY handshake for Runtime to guard against the
Lease transition window. Exit RunAll when all selected components are
not deployed rather than waiting for Ctrl+C.
Move "orkestra-konductor" to orktypes.KonductorLeaseName so a single
change point covers both the proxy and the konductor election setup.
Fix 08-komposer-registry README numbering and add ork run inspection step.
Add two mutation subcommands to the E2E kubectl DSL. Both trigger a
rollout and wait for completion (ready: true default, skippable via
ready: false). Neither appears in onFailure diagnostics.

Also add still-waiting feedback to the expect polling loop — prints
"still waiting [Xs]..." every 10s so long waits do not look like hangs.

Typical use: patch ConfigMap + kubectl.restart gateway to disable
deletion protection before cleanup, unblocking CR and CRD deletion.
@iAlexeze iAlexeze merged commit c89e601 into main Jul 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant