portforward: tunnel spdy through websockets
Kubernetes-commit: 8b447d8c97e8823b4308eb91cf7d75693e867c61
This commit is contained in:
parent
ecc9600ea2
commit
858d3a5b72
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/cli-runtime/pkg/genericiooptions"
|
"k8s.io/cli-runtime/pkg/genericiooptions"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
"k8s.io/client-go/kubernetes/scheme"
|
||||||
|
@ -50,7 +51,7 @@ import (
|
||||||
type PortForwardOptions struct {
|
type PortForwardOptions struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
PodName string
|
PodName string
|
||||||
RESTClient *restclient.RESTClient
|
RESTClient restclient.Interface
|
||||||
Config *restclient.Config
|
Config *restclient.Config
|
||||||
PodClient corev1client.PodsGetter
|
PodClient corev1client.PodsGetter
|
||||||
Address []string
|
Address []string
|
||||||
|
@ -99,11 +100,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCmdPortForward(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
|
func NewCmdPortForward(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
|
||||||
opts := &PortForwardOptions{
|
opts := NewDefaultPortForwardOptions(streams)
|
||||||
PortForwarder: &defaultPortForwarder{
|
|
||||||
IOStreams: streams,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "port-forward TYPE/NAME [options] [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
|
Use: "port-forward TYPE/NAME [options] [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
|
||||||
DisableFlagsInUseLine: true,
|
DisableFlagsInUseLine: true,
|
||||||
|
@ -123,6 +120,14 @@ func NewCmdPortForward(f cmdutil.Factory, streams genericiooptions.IOStreams) *c
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDefaultPortForwardOptions(streams genericiooptions.IOStreams) *PortForwardOptions {
|
||||||
|
return &PortForwardOptions{
|
||||||
|
PortForwarder: &defaultPortForwarder{
|
||||||
|
IOStreams: streams,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type portForwarder interface {
|
type portForwarder interface {
|
||||||
ForwardPorts(method string, url *url.URL, opts PortForwardOptions) error
|
ForwardPorts(method string, url *url.URL, opts PortForwardOptions) error
|
||||||
}
|
}
|
||||||
|
@ -137,6 +142,14 @@ func (f *defaultPortForwarder) ForwardPorts(method string, url *url.URL, opts Po
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, url)
|
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, method, url)
|
||||||
|
if cmdutil.PortForwardWebsockets.IsEnabled() {
|
||||||
|
tunnelingDialer, err := portforward.NewSPDYOverWebsocketDialer(url, opts.Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// First attempt tunneling (websocket) dialer, then fallback to spdy dialer.
|
||||||
|
dialer = portforward.NewFallbackDialer(tunnelingDialer, dialer, httpstream.IsUpgradeFailure)
|
||||||
|
}
|
||||||
fw, err := portforward.NewOnAddresses(dialer, opts.Address, opts.Ports, opts.StopChannel, opts.ReadyChannel, f.Out, f.ErrOut)
|
fw, err := portforward.NewOnAddresses(dialer, opts.Address, opts.Ports, opts.StopChannel, opts.ReadyChannel, f.Out, f.ErrOut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -387,7 +400,14 @@ func (o PortForwardOptions) Validate() error {
|
||||||
|
|
||||||
// RunPortForward implements all the necessary functionality for port-forward cmd.
|
// RunPortForward implements all the necessary functionality for port-forward cmd.
|
||||||
func (o PortForwardOptions) RunPortForward() error {
|
func (o PortForwardOptions) RunPortForward() error {
|
||||||
pod, err := o.PodClient.Pods(o.Namespace).Get(context.TODO(), o.PodName, metav1.GetOptions{})
|
return o.RunPortForwardContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunPortForwardContext implements all the necessary functionality for port-forward cmd.
|
||||||
|
// It ends portforwarding when an error is received from the backend, or an os.Interrupt
|
||||||
|
// signal is received, or the provided context is done.
|
||||||
|
func (o PortForwardOptions) RunPortForwardContext(ctx context.Context) error {
|
||||||
|
pod, err := o.PodClient.Pods(o.Namespace).Get(ctx, o.PodName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -401,7 +421,10 @@ func (o PortForwardOptions) RunPortForward() error {
|
||||||
defer signal.Stop(signals)
|
defer signal.Stop(signals)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-signals
|
select {
|
||||||
|
case <-signals:
|
||||||
|
case <-ctx.Done():
|
||||||
|
}
|
||||||
if o.StopChannel != nil {
|
if o.StopChannel != nil {
|
||||||
close(o.StopChannel)
|
close(o.StopChannel)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package portforward
|
package portforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -101,6 +102,8 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := &PortForwardOptions{}
|
opts := &PortForwardOptions{}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
cmd := NewCmdPortForward(tf, genericiooptions.NewTestIOStreamsDiscard())
|
cmd := NewCmdPortForward(tf, genericiooptions.NewTestIOStreamsDiscard())
|
||||||
cmd.Run = func(cmd *cobra.Command, args []string) {
|
cmd.Run = func(cmd *cobra.Command, args []string) {
|
||||||
if err = opts.Complete(tf, cmd, args); err != nil {
|
if err = opts.Complete(tf, cmd, args); err != nil {
|
||||||
|
@ -110,7 +113,7 @@ func testPortForward(t *testing.T, flags map[string]string, args []string) {
|
||||||
if err = opts.Validate(); err != nil {
|
if err = opts.Validate(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = opts.RunPortForward()
|
err = opts.RunPortForwardContext(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, value := range flags {
|
for name, value := range flags {
|
||||||
|
|
|
@ -430,6 +430,7 @@ const (
|
||||||
InteractiveDelete FeatureGate = "KUBECTL_INTERACTIVE_DELETE"
|
InteractiveDelete FeatureGate = "KUBECTL_INTERACTIVE_DELETE"
|
||||||
OpenAPIV3Patch FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
|
OpenAPIV3Patch FeatureGate = "KUBECTL_OPENAPIV3_PATCH"
|
||||||
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
|
RemoteCommandWebsockets FeatureGate = "KUBECTL_REMOTE_COMMAND_WEBSOCKETS"
|
||||||
|
PortForwardWebsockets FeatureGate = "KUBECTL_PORT_FORWARD_WEBSOCKETS"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsEnabled returns true iff environment variable is set to true.
|
// IsEnabled returns true iff environment variable is set to true.
|
||||||
|
|
Loading…
Reference in New Issue