kubectl-ate: dial ateapi directly over the port-forward stream#539
Draft
Yiqing Wang (YQ-Wang) wants to merge 1 commit into
Draft
kubectl-ate: dial ateapi directly over the port-forward stream#539Yiqing Wang (YQ-Wang) wants to merge 1 commit into
Yiqing Wang (YQ-Wang) wants to merge 1 commit into
Conversation
2 tasks
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.
Relates to #26 and #540. Opening this as a draft to ask whether the change is wanted before polishing it.
Summary
kubectl atereaches ateapi through client-go'sPortForwarder, which binds a localhost TCP listener, accepts a connection from our own gRPC client, and copies bytes between that connection and the Kubernetes port-forward stream. This exposes the port-forward stream directly as gRPC'snet.Conn, so the listener and the copy goroutines go away.That buys three things: no localhost listener bound for the lifetime of each command, gRPC able to redial transparently where the current single tunnel is a permanent failure if it drops, and one less copy hop per RPC. It also removes the reset behind #26 at its source, since that copy loop is what observes the RST — but #540 keeps the message away from users regardless, so this should stand or fall as a transport change.
The adapter follows Kubernetes' own direct-stream pattern, which is not importable: https://github.com/kubernetes/kubernetes/blob/v1.36.0/test/e2e/framework/pod/dial.go
Questions I would like input on
net.Conn? Anyone wanting gRPC over port-forward reimplements this today.internal/e2e/portforward.gois a second consumer that could share it.Not ready for a detailed review
watchErrorsreports throughslog.Error, and sincekubectl-ateconfigures no handler that lands on the user's terminal as2026/07/25 02:41:27 ERROR Port-forward connection failed remotePort=443 error="...". That is the same kind of message as #26, and #540 does not suppress it because #540 replacesutilruntime.ErrorHandlersrather than touching slog. Closing the connection and letting gRPC report the transport error is the better shape.Beyond that: the deadline methods are no-ops that grpc-go's teardown relies on (grpc/grpc-go#8425),
CloseevaluatesStream.Close()beforestreamConn.Close()without theReset()that upstream uses to discard unsent data,RemoveStreamsis never called, the now-unusedcancelfield can go, and moving dial failures from client construction to the first RPC needs a UX check for cases like an unready api pod or an RBAC denial.Validation
go test -race -count=100 ./internal/ateclient,go test ./cmd/kubectl-ate/...,go mod tidy -diffandmake verifypass. Coverage is fake-based.The transport has not been exercised against a live cluster at volume yet; that and an e2e assertion that a successful command leaves stderr clean belong on the list above. Note that the reset itself is timing-dependent and 1,500 CLI invocations on macOS/kind produced none, so a load test has to run on Linux to say anything.
Moving port-forward transport to WebSockets would not eliminate this reset class: kubernetes/kubectl#1620 (comment)