feat: ork proxy — port-forward CLI for Helm-deployed Orkestra; e2e Go client migration#203
Merged
Conversation
…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.
…ration, kind spinner fix
…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.
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.
Summary
ork proxy— a single command that replaces manualkubectl port-forwardcalls when working with a Helm-deployed Orkestra. Discovers components by the neworkestra.orkspace.io/komponentlabel, resolves the Runtime leader via theorkestra-konductorLease, and keeps all forwards alive with automatic reconnection.SelfSubjectAccessReviewfor auth checks, and SPDY portforward forkubectl.port-forwardassertions.kubectl.restartandkubectl.scaleE2E DSL subcommands for rollout mutations in test sequences.Changed files
cmd/cli/proxy.go(new)ork proxycommand.--forflag uses same vocabulary asork 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 viasignal.NotifyContext.pkg/proxy/(new)discover.go—FindServicebyorkestra.orkspace.io/komponentlabel;ResolvePodfrom Service selector;ResolveRuntimePodviaorkestra-konductorLeaseproxy.go—RunAllblocks until ctx cancelled;forwardWithReconnectreconnect loop per goroutine;forwardSPDY portforward viak8s.io/client-go/tools/portforward; coloured output usingpkg/utilshelpers; proactive pod watch callsstop()on pod disappearance;probeReadyloops on/healthbefore marking Runtime forward live;↺reconnecting indicator; exits when all targets are not deployedports.go—CheckPortTCP bind checkREADME.md,docs/01-discovery.md,docs/02-forward.mdpkg/types/types.goKonductorLeaseName = "orkestra-konductor"constant shared bypkg/proxy/discover.goandpkg/konductor/konduct.go.charts/orkestra/templates/Added
orkestra.orkspace.io/komponentlabel 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.goRunnergainscs kubernetes.Interfaceandcfg *rest.Configbuilt afterensureClusterviabuildClient(). Threaded through toverifyExpectationandrunOnFailure. Still-waiting feedback: printsstill waiting [Xs]...every 10 s while a step is polling so long waits do not look like hangs.pkg/e2e/verify.goresolveLeaderHolder—CoordinationV1().Leases().Get()replaceskubectl get lease -o jsonpath;workDirparam droppedcheckKubectlAuth—SelfSubjectAccessReviewwhene.As == ""(matcheskubectl auth can-idefault behaviour);SubjectAccessReviewwithUserset when--asis specifiedcheckKubectlPortForward— SPDY portforward +net/http;readyChaneliminates the old curl-retry polling race; pod resolved via lease, service selector, or direct namecheckKubectlRestart—kubectl rollout restart <kind>/<name>+ optionalrollout status;ready: truedefaultcheckKubectlScale—kubectl scale <kind>/<name> --replicas=N+ optionalrollout status; same defaultpkg/e2e/validate.govalidateKubectlRestartandvalidateKubectlScale— kind and name required.pkg/types/e2e.goE2EKubectlgainsRestart []E2EKubectlRestartandScale []E2EKubectlScale. Both structs:kind,name,namespace,ready *bool.pkg/e2e/onfailure.goReceives
cs kubernetes.Interface;resolveLeaderHoldercall sites updated.restartandscaleare mutations — not emitted in onFailure diagnostics.pkg/ork/kind.gowaitForNodesReadysignature gainsonNodeReady func(nodeName string)callback. Output captured viaos.Pipeinto 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.goBinds the listener synchronously before launching the goroutine. A port conflict now returns an error from
Start()soork runfails immediately instead of silently continuing with a dead health server.documentation/reference/cli/proxy.md(new),index.mdCLI reference page for
ork proxy. Added to the Tooling section of the index.documentation/reference/schema/04-e2e/07-kubectl.mdkubectl.restartandkubectl.scalesections with field tables and examples.examples/security/deletion-protection/e2e.yamlCleanup step: apply ConfigMap with
deletionProtection.enabled: false+kubectl.restartgateway. The housekeeper readsenabled: falseon 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 withork proxyacross documentation and example READMEs. Gateway-specific pages useork proxy --for gatewaywith curl onlocalhost:8443. Cross-binary example usesork proxy -n producer-system.Key design decisions
Label-based discovery.
ork proxyfinds components byorkestra.orkspace.io/komponentrather than hardcoded service names, keeping discovery decoupled from chart naming conventions. The label useskomponent(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 proxyreads theorkestra-konductorLease to resolve the leader pod, so the forwarded connection always reaches the replica with authoritative reconciler state — consistent with howork e2etargets the leader.SelfSubjectAccessReviewnotSubjectAccessReview.kubectl auth can-iwithout--assends aSelfSubjectAccessReview— the API server infers the caller's identity.SubjectAccessReviewwith an emptyUserfield is rejected. The migration now matches kubectl's actual behaviour.restart/scaleare mutations, not assertions. They do not appear inonFailurediagnostics. Theready: truedefault runsrollout statusto confirm the workload settled — setready: falseto 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-katalogConfigMap withenabled: falseand restart the gateway — the housekeeper then callscleanupValidatingWebhookon startup and never re-registers.Inspired by
kubectl proxy. The command name and concept followkubectl proxy— a dedicated proxy command for the tool's own components rather than a generic port-forward wrapper.