remote command turn on feature gates
Kubernetes-commit: a147693deb2e7f040cf367aae4a7ae5d1cb3e7aa
This commit is contained in:
parent
acf4a09f2d
commit
2b8ea3563c
|
|
@ -158,23 +158,10 @@ type DefaultRemoteAttach struct{}
|
||||||
|
|
||||||
// Attach executes attach to a running container
|
// Attach executes attach to a running container
|
||||||
func (*DefaultRemoteAttach) Attach(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
func (*DefaultRemoteAttach) Attach(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||||
// Legacy SPDY executor is default. If feature gate enabled, fallback
|
exec, err := createExecutor(url, config)
|
||||||
// executor attempts websockets first--then SPDY.
|
|
||||||
exec, err := remotecommand.NewSPDYExecutor(config, "POST", url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cmdutil.RemoteCommandWebsockets.IsEnabled() {
|
|
||||||
// WebSocketExecutor must be "GET" method as described in RFC 6455 Sec. 4.1 (page 17).
|
|
||||||
websocketExec, err := remotecommand.NewWebSocketExecutor(config, "GET", url.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
exec, err = remotecommand.NewFallbackExecutor(websocketExec, exec, httpstream.IsUpgradeFailure)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
|
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
|
||||||
Stdin: stdin,
|
Stdin: stdin,
|
||||||
Stdout: stdout,
|
Stdout: stdout,
|
||||||
|
|
@ -184,6 +171,27 @@ func (*DefaultRemoteAttach) Attach(url *url.URL, config *restclient.Config, stdi
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createExecutor returns the Executor or an error if one occurred.
|
||||||
|
func createExecutor(url *url.URL, config *restclient.Config) (remotecommand.Executor, error) {
|
||||||
|
exec, err := remotecommand.NewSPDYExecutor(config, "POST", url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Fallback executor is default, unless feature flag is explicitly disabled.
|
||||||
|
if !cmdutil.RemoteCommandWebsockets.IsDisabled() {
|
||||||
|
// WebSocketExecutor must be "GET" method as described in RFC 6455 Sec. 4.1 (page 17).
|
||||||
|
websocketExec, err := remotecommand.NewWebSocketExecutor(config, "GET", url.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exec, err = remotecommand.NewFallbackExecutor(websocketExec, exec, httpstream.IsUpgradeFailure)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exec, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Complete verifies command line arguments and loads data from the command environment
|
// Complete verifies command line arguments and loads data from the command environment
|
||||||
func (o *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
func (o *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
"k8s.io/kubectl/pkg/cmd/exec"
|
"k8s.io/kubectl/pkg/cmd/exec"
|
||||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
"k8s.io/kubectl/pkg/cmd/util/podcmd"
|
||||||
"k8s.io/kubectl/pkg/polymorphichelpers"
|
"k8s.io/kubectl/pkg/polymorphichelpers"
|
||||||
"k8s.io/kubectl/pkg/scheme"
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
|
|
@ -553,3 +554,37 @@ func TestReattachMessage(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateExecutor(t *testing.T) {
|
||||||
|
url, err := url.Parse("http://localhost:8080/index.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to parse test url: %v", err)
|
||||||
|
}
|
||||||
|
config := cmdtesting.DefaultClientConfig()
|
||||||
|
// First, ensure that no environment variable creates the fallback executor.
|
||||||
|
executor, err := createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); !isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
// Next, check turning on feature flag explicitly also creates fallback executor.
|
||||||
|
t.Setenv(string(cmdutil.RemoteCommandWebsockets), "true")
|
||||||
|
executor, err = createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); !isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
// Finally, check explicit disabling does NOT create the fallback executor.
|
||||||
|
t.Setenv(string(cmdutil.RemoteCommandWebsockets), "false")
|
||||||
|
executor, err = createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,23 +121,10 @@ type RemoteExecutor interface {
|
||||||
type DefaultRemoteExecutor struct{}
|
type DefaultRemoteExecutor struct{}
|
||||||
|
|
||||||
func (*DefaultRemoteExecutor) Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
func (*DefaultRemoteExecutor) Execute(url *url.URL, config *restclient.Config, stdin io.Reader, stdout, stderr io.Writer, tty bool, terminalSizeQueue remotecommand.TerminalSizeQueue) error {
|
||||||
// Legacy SPDY executor is default. If feature gate enabled, fallback
|
exec, err := createExecutor(url, config)
|
||||||
// executor attempts websockets first--then SPDY.
|
|
||||||
exec, err := remotecommand.NewSPDYExecutor(config, "POST", url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cmdutil.RemoteCommandWebsockets.IsEnabled() {
|
|
||||||
// WebSocketExecutor must be "GET" method as described in RFC 6455 Sec. 4.1 (page 17).
|
|
||||||
websocketExec, err := remotecommand.NewWebSocketExecutor(config, "GET", url.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
exec, err = remotecommand.NewFallbackExecutor(websocketExec, exec, httpstream.IsUpgradeFailure)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
|
return exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
|
||||||
Stdin: stdin,
|
Stdin: stdin,
|
||||||
Stdout: stdout,
|
Stdout: stdout,
|
||||||
|
|
@ -147,6 +134,27 @@ func (*DefaultRemoteExecutor) Execute(url *url.URL, config *restclient.Config, s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createExecutor returns the Executor or an error if one occurred.
|
||||||
|
func createExecutor(url *url.URL, config *restclient.Config) (remotecommand.Executor, error) {
|
||||||
|
exec, err := remotecommand.NewSPDYExecutor(config, "POST", url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Fallback executor is default, unless feature flag is explicitly disabled.
|
||||||
|
if !cmdutil.RemoteCommandWebsockets.IsDisabled() {
|
||||||
|
// WebSocketExecutor must be "GET" method as described in RFC 6455 Sec. 4.1 (page 17).
|
||||||
|
websocketExec, err := remotecommand.NewWebSocketExecutor(config, "GET", url.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exec, err = remotecommand.NewFallbackExecutor(websocketExec, exec, httpstream.IsUpgradeFailure)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exec, nil
|
||||||
|
}
|
||||||
|
|
||||||
type StreamOptions struct {
|
type StreamOptions struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
PodName string
|
PodName string
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ import (
|
||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/rest/fake"
|
"k8s.io/client-go/rest/fake"
|
||||||
"k8s.io/client-go/tools/remotecommand"
|
"k8s.io/client-go/tools/remotecommand"
|
||||||
|
|
||||||
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
"k8s.io/kubectl/pkg/scheme"
|
"k8s.io/kubectl/pkg/scheme"
|
||||||
"k8s.io/kubectl/pkg/util/term"
|
"k8s.io/kubectl/pkg/util/term"
|
||||||
)
|
)
|
||||||
|
|
@ -402,3 +402,37 @@ func TestSetupTTY(t *testing.T) {
|
||||||
t.Errorf("attach stdin, TTY, is a terminal: tty.Out should equal o.Out")
|
t.Errorf("attach stdin, TTY, is a terminal: tty.Out should equal o.Out")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateExecutor(t *testing.T) {
|
||||||
|
url, err := url.Parse("http://localhost:8080/index.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to parse test url: %v", err)
|
||||||
|
}
|
||||||
|
config := cmdtesting.DefaultClientConfig()
|
||||||
|
// First, ensure that no environment variable creates the fallback executor.
|
||||||
|
executor, err := createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); !isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
// Next, check turning on feature flag explicitly also creates fallback executor.
|
||||||
|
t.Setenv(string(cmdutil.RemoteCommandWebsockets), "true")
|
||||||
|
executor, err = createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); !isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
// Finally, check explicit disabling does NOT create the fallback executor.
|
||||||
|
t.Setenv(string(cmdutil.RemoteCommandWebsockets), "false")
|
||||||
|
executor, err = createExecutor(url, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create executor: %v", err)
|
||||||
|
}
|
||||||
|
if _, isFallback := executor.(*remotecommand.FallbackExecutor); isFallback {
|
||||||
|
t.Errorf("expected fallback executor, got %#v", executor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue