Skip to content

[WIP] kubectl exec/cp/attach over WebSocket#116778

Closed
seans3 wants to merge 8 commits into
kubernetes:masterfrom
seans3:kubectl-websocket
Closed

[WIP] kubectl exec/cp/attach over WebSocket#116778
seans3 wants to merge 8 commits into
kubernetes:masterfrom
seans3:kubectl-websocket

Conversation

@seans3

@seans3 seans3 commented Mar 20, 2023

Copy link
Copy Markdown
Contributor
  • Important - based on previous @ash2k PR: [WIP] Kubectl exec/attach/cp over WebSocket #110142

  • Proof-of-Concept PR which transitions kubectl exec, kubectl cp, and kubectl attach from using SPDY to WebSockets for the bi-directional streaming protocol communicating between k8s clients and the API Server.

  • Creates WebSockets client (remotecommand.NewWebSocketExecutor).

  • Creates StreamTranslator proxy within the API Server. This proxy contains a WebSockets server and a SPDY client. It terminates the WebSockets communication from the client, sending the data into a SPDY client which is connected upstream to kubelet. In this way, the client to API Server communication uses WebSockets, while communication upstream does not substantially change, continuing to use the SPDY protocol.

  • Manually tested successfully with the following commands using the nginx pod:

    • $ ./kubectl exec nginx -- date (basic command with STDOUT)
    • $ echo "this is a test" | ./kubectl exec -i nginx -- cat (pipe STDIN to command running on container)
    • $ ./kubectl cp ~/deploy-1.yaml nginx:/ (cp file from local to container)
    • $ ./kubectl cp nginx:/product_name /tmp/product_name (cp file from container to local)
    • $ ./kubectl exec -it nginx -- bash (interactive terminal)
  • TODO (as of May 1st, 2023)

    • Implement client/APIServer handshake/fallback to determine which streaming protocol to use and to ensure correctness during client/server version skew.
    • Understand why some of the e2e tests are failing.
    • Implement a heartbeat within the WebSockets protocol (Ping/Pong).
    • Implement Reset the logical stream for a channel within the WebSockets protocol. Reset means no data is coming AND no data should be sent (it will be discarded).
    • Refactor WebSockets server code in apiserver and SPDY client code in client-go into apimachinery, since the proxy located in apimachinery needs this code, while apiserver and client-go code can not be imported into apimachinery. NOTE Currently, since this is a POC PR, the WebSockets server code and SPDY client code has been copied into apimachinery.

/kind feature
/sig api-machinery

Fixes: #89163

Example kubectl exec over WebSocket

$ ./kubectl exec -v=7 nginx -- sh -c 'ls -l'
I0320 15:07:05.414379  993377 loader.go:373] Config loaded from file:  /home/sean/.kube/config
I0320 15:07:05.418742  993377 round_trippers.go:463] GET https://127.0.0.1:45051/api/v1/namespaces/default/pods/nginx
I0320 15:07:05.418760  993377 round_trippers.go:469] Request Headers:
I0320 15:07:05.418775  993377 round_trippers.go:473]     Accept: application/json, */*
I0320 15:07:05.418787  993377 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/c7c075a
I0320 15:07:05.428823  993377 round_trippers.go:574] Response Status: 200 OK in 10 milliseconds
I0320 15:07:05.431447  993377 podcmd.go:88] Defaulting container name to nginx
I0320 15:07:05.431948  993377 round_trippers.go:463] POST https://127.0.0.1:45051/api/v1/namespaces/default/pods/nginx/exec?command=sh&command=-c&command=ls+-l&container=nginx&stderr=true&stdout=true
I0320 15:07:05.431958  993377 round_trippers.go:469] Request Headers:
I0320 15:07:05.431968  993377 round_trippers.go:473]     X-Stream-Protocol-Version: v4.channel.k8s.io
I0320 15:07:05.431977  993377 round_trippers.go:473]     X-Stream-Protocol-Version: channel.k8s.io
I0320 15:07:05.431986  993377 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/c7c075a
I0320 15:07:05.450470  993377 round_trippers.go:574] Response Status: 101 Switching Protocols in 18 milliseconds
I0320 15:07:05.450488  993377 remotecommand_websocket.go:102] The protocol is v4.channel.k8s.io
total 88
drwxr-xr-x   2 root root 4096 Feb 27 00:00 bin
drwxr-xr-x   2 root root 4096 Dec  9 19:15 boot
drwxr-xr-x   5 root root  360 Mar 19 21:22 dev
drwxr-xr-x   1 root root 4096 Mar  1 18:43 docker-entrypoint.d
-rwxrwxr-x   1 root root 1616 Mar  1 18:42 docker-entrypoint.sh
drwxr-xr-x   1 root root 4096 Mar 19 21:22 etc
drwxr-xr-x   2 root root 4096 Dec  9 19:15 home
drwxr-xr-x   1 root root 4096 Feb 27 00:00 lib
drwxr-xr-x   2 root root 4096 Feb 27 00:00 lib64
drwxr-xr-x   2 root root 4096 Feb 27 00:00 media
drwxr-xr-x   2 root root 4096 Feb 27 00:00 mnt
drwxr-xr-x   2 root root 4096 Feb 27 00:00 opt
dr-xr-xr-x 689 root root    0 Mar 19 21:22 proc
-rw-r--r--   1 root root    5 Mar 19 21:22 product_name
-rw-r--r--   1 root root   37 Mar 19 21:22 product_uuid
drwx------   1 root root 4096 Mar 19 23:53 root
drwxr-xr-x   1 root root 4096 Mar 19 21:22 run
drwxr-xr-x   2 root root 4096 Feb 27 00:00 sbin
drwxr-xr-x   2 root root 4096 Feb 27 00:00 srv
dr-xr-xr-x  13 root root    0 Mar 19 21:22 sys
drwxrwxrwt   1 root root 4096 Mar  1 18:43 tmp
drwxr-xr-x   1 root root 4096 Feb 27 00:00 usr
drwxr-xr-x   1 root root 4096 Feb 27 00:00 var

Example kubectl cp using WebSockets from container to local

$ ./kubectl cp -v=7 nginx:/product_name /tmp/product_name
I0320 15:33:35.291087  997753 loader.go:373] Config loaded from file:  /home/sean/.kube/config
I0320 15:33:35.292420  997753 round_trippers.go:463] GET https://127.0.0.1:45051/api/v1/namespaces/default/pods/nginx
I0320 15:33:35.292429  997753 round_trippers.go:469] Request Headers:
I0320 15:33:35.292440  997753 round_trippers.go:473]     Accept: application/json, */*
I0320 15:33:35.292448  997753 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/3a3dee2
I0320 15:33:35.302483  997753 round_trippers.go:574] Response Status: 200 OK in 9 milliseconds
I0320 15:33:35.305260  997753 podcmd.go:88] Defaulting container name to nginx
I0320 15:33:35.305846  997753 round_trippers.go:463] POST https://127.0.0.1:45051/api/v1/namespaces/default/pods/nginx/exec?command=tar&command=cf&command=-&command=%2Fproduct_name&container=nginx&stderr=true&stdout=true
I0320 15:33:35.305857  997753 round_trippers.go:469] Request Headers:
I0320 15:33:35.305868  997753 round_trippers.go:473]     X-Stream-Protocol-Version: v4.channel.k8s.io
I0320 15:33:35.305878  997753 round_trippers.go:473]     X-Stream-Protocol-Version: channel.k8s.io
I0320 15:33:35.305889  997753 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/3a3dee2
I0320 15:33:35.319818  997753 round_trippers.go:574] Response Status: 101 Switching Protocols in 13 milliseconds
I0320 15:33:35.319834  997753 remotecommand_websocket.go:102] The protocol is v4.channel.k8s.io
$ ls -l /tmp/product_name 
-rw-rw-r-- 1 sean sean 5 Mar 20 15:33 /tmp/product_name

Example kubectl cp using WebSockets from local to container

$ ./kubectl cp -v=7 ~/deploy-1.yaml nginx:/
I0501 15:38:48.608662 1144027 loader.go:373] Config loaded from file:  /home/sean/.kube/config
I0501 15:38:48.609405 1144027 round_trippers.go:463] GET https://127.0.0.1:38083/api/v1/namespaces/default/pods/nginx
I0501 15:38:48.609411 1144027 round_trippers.go:469] Request Headers:
I0501 15:38:48.609417 1144027 round_trippers.go:473]     Accept: application/json, */*
I0501 15:38:48.609421 1144027 round_trippers.go:473]     User-Agent: kubectl/v1.28.0 (linux/amd64) kubernetes/3d18e09
I0501 15:38:48.622512 1144027 round_trippers.go:574] Response Status: 200 OK in 13 milliseconds
I0501 15:38:48.624366 1144027 podcmd.go:88] Defaulting container name to nginx
I0501 15:38:48.624790 1144027 round_trippers.go:463] POST https://127.0.0.1:38083/api/v1/namespaces/default/pods/nginx/exec?command=test&command=-d&command=%2F&container=nginx&stderr=true&stdout=true
I0501 15:38:48.624798 1144027 round_trippers.go:469] Request Headers:
I0501 15:38:48.624808 1144027 round_trippers.go:473]     X-Stream-Protocol-Version: v4.channel.k8s.io
I0501 15:38:48.624816 1144027 round_trippers.go:473]     X-Stream-Protocol-Version: channel.k8s.io
I0501 15:38:48.624824 1144027 round_trippers.go:473]     User-Agent: kubectl/v1.28.0 (linux/amd64) kubernetes/3d18e09
I0501 15:38:48.637062 1144027 round_trippers.go:574] Response Status: 101 Switching Protocols in 12 milliseconds
I0501 15:38:48.637076 1144027 remotecommand_websocket.go:104] The protocol is v4.channel.k8s.io
I0501 15:38:48.728970 1144027 round_trippers.go:463] GET https://127.0.0.1:38083/api/v1/namespaces/default/pods/nginx
I0501 15:38:48.728989 1144027 round_trippers.go:469] Request Headers:
I0501 15:38:48.729004 1144027 round_trippers.go:473]     User-Agent: kubectl/v1.28.0 (linux/amd64) kubernetes/3d18e09
I0501 15:38:48.729016 1144027 round_trippers.go:473]     Accept: application/json, */*
I0501 15:38:48.731436 1144027 round_trippers.go:574] Response Status: 200 OK in 2 milliseconds
I0501 15:38:48.731794 1144027 podcmd.go:88] Defaulting container name to nginx
I0501 15:38:48.732395 1144027 round_trippers.go:463] POST https://127.0.0.1:38083/api/v1/namespaces/default/pods/nginx/exec?command=tar&command=-xmf&command=-&command=-C&command=%2F&container=nginx&stderr=true&stdin=true&stdout=true
I0501 15:38:48.732406 1144027 round_trippers.go:469] Request Headers:
I0501 15:38:48.732418 1144027 round_trippers.go:473]     X-Stream-Protocol-Version: v4.channel.k8s.io
I0501 15:38:48.732430 1144027 round_trippers.go:473]     X-Stream-Protocol-Version: channel.k8s.io
I0501 15:38:48.732442 1144027 round_trippers.go:473]     User-Agent: kubectl/v1.28.0 (linux/amd64) kubernetes/3d18e09
I0501 15:38:48.744601 1144027 round_trippers.go:574] Response Status: 101 Switching Protocols in 12 milliseconds
I0501 15:38:48.744618 1144027 remotecommand_websocket.go:104] The protocol is v4.channel.k8s.io
I0501 15:38:48.744739 1144027 remotecommand_websocket.go:235] Write() on stream 0
I0501 15:38:48.744770 1144027 remotecommand_websocket.go:259] Write() done on stream 0
I0501 15:38:48.744799 1144027 remotecommand_websocket.go:235] Write() on stream 0
I0501 15:38:48.744857 1144027 remotecommand_websocket.go:259] Write() done on stream 0
I0501 15:38:48.744873 1144027 remotecommand_websocket.go:235] Write() on stream 0
I0501 15:38:48.744890 1144027 remotecommand_websocket.go:259] Write() done on stream 0
I0501 15:38:48.744899 1144027 remotecommand_websocket.go:235] Write() on stream 0
I0501 15:38:48.744919 1144027 remotecommand_websocket.go:259] Write() done on stream 0
I0501 15:38:48.744940 1144027 remotecommand_websocket.go:235] Write() on stream 0
I0501 15:38:48.744974 1144027 remotecommand_websocket.go:259] Write() done on stream 0
I0501 15:38:48.744985 1144027 remotecommand_websocket.go:264] Close() on stream 0
I0501 15:38:48.744992 1144027 remotecommand_websocket.go:276] Sending half-close signal on stream 0
I0501 15:38:48.745014 1144027 remotecommand_websocket.go:280] Close() done on stream 0
$ ./kubectl exec nginx -- sh -c 'ls -l /deploy-1.yaml'
-rw-rw-r-- 1 1000 1000 425 May  1 22:38 /deploy-1.yaml

Example interactive use of kubectl exec over WebSockets

$ ./kubectl exec nginx -it -- bash
root@nginx:/# whoami
root
root@nginx:/# echo "kubectl over websockets"
kubectl over websockets
root@nginx:/# exit
exit

Example kubectl exec over WebSockets using STDIN

$ echo "websockets-test" | ./kubectl exec -v=7 -i nginx -- cat
I0321 20:49:11.492057  572622 loader.go:373] Config loaded from file:  /usr/local/google/home/seans/.kube/config
I0321 20:49:11.501332  572622 round_trippers.go:463] GET https://127.0.0.1:41129/api/v1/namespaces/default/pods/nginx
I0321 20:49:11.501367  572622 round_trippers.go:469] Request Headers:
I0321 20:49:11.501379  572622 round_trippers.go:473]     Accept: application/json, */*
I0321 20:49:11.501389  572622 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/3a3dee2
I0321 20:49:11.511884  572622 round_trippers.go:574] Response Status: 200 OK in 10 milliseconds
I0321 20:49:11.514230  572622 podcmd.go:88] Defaulting container name to nginx
I0321 20:49:11.514808  572622 round_trippers.go:463] POST https://127.0.0.1:41129/api/v1/namespaces/default/pods/nginx/exec?command=cat&container=nginx&stderr=true&stdin=true&stdout=true
I0321 20:49:11.514830  572622 round_trippers.go:469] Request Headers:
I0321 20:49:11.514846  572622 round_trippers.go:473]     X-Stream-Protocol-Version: v4.channel.k8s.io
I0321 20:49:11.514859  572622 round_trippers.go:473]     X-Stream-Protocol-Version: channel.k8s.io
I0321 20:49:11.514920  572622 round_trippers.go:473]     User-Agent: kubectl/v1.27.0 (linux/amd64) kubernetes/3a3dee2
I0321 20:49:11.537557  572622 round_trippers.go:574] Response Status: 101 Switching Protocols in 22 milliseconds
I0321 20:49:11.537606  572622 remotecommand_websocket.go:102] The protocol is v4.channel.k8s.io
I0321 20:49:11.537878  572622 remotecommand_websocket.go:233] Write() on stream 0
I0321 20:49:11.537959  572622 remotecommand_websocket.go:257] Write() done on stream 0
I0321 20:49:11.537980  572622 remotecommand_websocket.go:262] Close() on stream 0
I0321 20:49:11.537980  572622 remotecommand_websocket.go:276] Sending half-close signal on stream 0
I0321 20:49:11.537997  572622 remotecommand_websocket.go:268] Close() done on stream 0
websockets-test
$
NONE

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note-none Denotes a PR that doesn't merit a release note. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. kind/feature Categorizes issue or PR as related to a new feature. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. area/e2e-test-framework Issues or PRs related to refactoring the kubernetes e2e test framework labels Mar 20, 2023
@k8s-ci-robot k8s-ci-robot added area/kubectl needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. area/kubelet area/test needs-priority Indicates a PR lacks a `priority/foo` label and requires one. sig/cli Categorizes an issue or PR as relevant to SIG CLI. sig/node Categorizes an issue or PR as relevant to SIG Node. sig/testing Categorizes an issue or PR as relevant to SIG Testing. labels Mar 20, 2023
@seans3 seans3 changed the title [WIP] exec/cp/attach over WebSocket [WIP] kubectl exec/cp/attach over WebSocket Mar 20, 2023
@seans3 seans3 force-pushed the kubectl-websocket branch from 69ae4f3 to c7c075a Compare March 20, 2023 21:02
@aojea

aojea commented Mar 20, 2023

Copy link
Copy Markdown
Member

/cc

@k8s-ci-robot k8s-ci-robot requested a review from aojea March 20, 2023 21:02
@seans3 seans3 force-pushed the kubectl-websocket branch from c7c075a to 3a3dee2 Compare March 20, 2023 22:13
@seans3

seans3 commented Mar 21, 2023

Copy link
Copy Markdown
Contributor Author

/assign @brianpursley
/assign @ardaguclu
/assign @mpuckett159

@cici37

cici37 commented Mar 21, 2023

Copy link
Copy Markdown
Contributor

/remove-sig api-machinery

@k8s-ci-robot k8s-ci-robot removed the sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. label Mar 21, 2023
@mpuckett159

Copy link
Copy Markdown
Contributor

Just a reminder that the gorilla project as a whole has been archived. I think we should look at alternatives for that library for using websocket APIs. It seems like the nhooyr/websocket library is our best option for this? Unsure what others think. Still need to give the PR a review in general.

Another option would be gobwas/ws.

I'm not too sure how active either of these libraries are unfortunately. It looks like the last release for both libraries was in 2021.

@brianpursley brianpursley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't had a chance to go through this completely yet, and haven't tried it out yet, but I did take a quick look at the code this evening and left a few comments.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like ctx is never used.

Would it make sense to move the Stream's implementation into StreamWithContext and propagate ctx to other calls like http.NewRequestWithContext?

Then, Stream would just be something like this:

func (e *wsStreamExecutor) Stream(options StreamOptions) error {
	return e.StreamWithContext(context.TODO(), options)
}

func (e *wsStreamExecutor) StreamWithContext(ctx context.Context, options StreamOptions) error {
	req, err := http.NewRequestWithContext(ctx, e.method, e.url, nil)
        ...

It doesn't look like there is any way to pass context to streamer.stream() so I don't know how useful it really is without being able to do that, but it seems like if it takes context, it should try to do something with it if possible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently ioutil.ReadAll has been deprecated. Might as well use io.ReadAll I guess.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this TODO was brought over from spdy/roundtripper, but it has 7+ years old. Is there anything that needs to be done for this, or should it just be removed?

Or at least maybe provide some additional context about what actually needs to be done, perhaps turning this into a github issue instead of a TODO comment, so hopefully it doesn't remaing a TODO comment for another 7 years.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we be using exec.StreamWithContext() here (and the other two places)?

Per this comment, exec.Stream() is deprecated:

// Deprecated: use StreamWithContext instead to avoid possible resource leaks.
// See https://github.com/kubernetes/kubernetes/pull/103177 for details.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something feels a little bit off about this.

For one thing, the interface name of streamCreator might not make sense anymore, as it now does more than just create streams.

What also strikes me is that SPDY has a "do nothing" implementation of Run() which tells me this really is specific to websockets.

I wonder if there is some way to not add Run to the interface and just make it be part of wsStreamExecutor's internal implementation.

For example, maybe do it inside wsStreamExecutor.StreamWithContext(), so then its existence is kept inside the websocket-specific implementation...

	creator := newWSStreamCreator(conn)
	go creator.run()
	return streamer.stream(creator)

Just a thought. I'm not trying to redesign things here, but noticed this interface change and thought maybe there was something not generic enough about it to be on an interface.

@seans3 seans3 force-pushed the kubectl-websocket branch from f6356f2 to 67126da Compare June 6, 2023 15:48
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 7, 2023
@seans3 seans3 force-pushed the kubectl-websocket branch from 67126da to c28a2b5 Compare June 10, 2023 00:26
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 10, 2023
@seans3 seans3 force-pushed the kubectl-websocket branch from c28a2b5 to 750ff46 Compare June 10, 2023 00:28
@k8s-ci-robot k8s-ci-robot added area/cloudprovider area/kube-proxy sig/architecture Categorizes an issue or PR as relevant to SIG Architecture. sig/auth Categorizes an issue or PR as relevant to SIG Auth. sig/cloud-provider Categorizes an issue or PR as relevant to SIG Cloud Provider. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. sig/instrumentation Categorizes an issue or PR as relevant to SIG Instrumentation. sig/network Categorizes an issue or PR as relevant to SIG Network. sig/storage Categorizes an issue or PR as relevant to SIG Storage. labels Jun 10, 2023
@aojea

aojea commented Jun 10, 2023

Copy link
Copy Markdown
Member

/assign @aojea

please ping me when is ready for review

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jun 19, 2023
@belgaied2

Copy link
Copy Markdown

Do we have news about this feature? We have issues with external WAFs refusing kubectl exec commands just because support of SPDY/3.1 has been dropped on the WAF!

@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: seans3
Once this PR has been reviewed and has the lgtm label, please assign deads2k, dims for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@seans3

seans3 commented Jul 16, 2023

Copy link
Copy Markdown
Contributor Author

This PR was broken up into two smaller, more manageable PRs:

  1. WebSocket Client and V5 RemoteCommand Subprotocol: WebSocket Client and V5 RemoteCommand Subprotocol #119157
  2. StreamTranslator, FallbackExecutor, and feature flags: Stream Translator Proxy and FallbackExecutor for WebSockets #119186

Closing this Proof-of-Concept PR.

@seans3

seans3 commented Jul 16, 2023

Copy link
Copy Markdown
Contributor Author

Do we have news about this feature? We have issues with external WAFs refusing kubectl exec commands just because support of SPDY/3.1 has been dropped on the WAF!

Please see the following two PR's:
#119157
#119186

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apiserver area/cloudprovider area/e2e-test-framework Issues or PRs related to refactoring the kubernetes e2e test framework area/kube-proxy area/kubectl area/kubelet area/test cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/feature Categorizes issue or PR as related to a new feature. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. release-note-none Denotes a PR that doesn't merit a release note. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/architecture Categorizes an issue or PR as relevant to SIG Architecture. sig/auth Categorizes an issue or PR as relevant to SIG Auth. sig/cli Categorizes an issue or PR as relevant to SIG CLI. sig/cloud-provider Categorizes an issue or PR as relevant to SIG Cloud Provider. sig/cluster-lifecycle Categorizes an issue or PR as relevant to SIG Cluster Lifecycle. sig/instrumentation Categorizes an issue or PR as relevant to SIG Instrumentation. sig/network Categorizes an issue or PR as relevant to SIG Network. sig/node Categorizes an issue or PR as relevant to SIG Node. sig/storage Categorizes an issue or PR as relevant to SIG Storage. sig/testing Categorizes an issue or PR as relevant to SIG Testing. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on.

Projects

Archived in project
Archived in project

Development

Successfully merging this pull request may close these issues.

Support kubectl exec/attach using Websocket