Attach must only allow a tty when container supports it

pull/6/head
Fabiano Franz 2015-10-29 18:32:58 -02:00
parent 5881c3c848
commit c60f19a1f8
2 changed files with 89 additions and 6 deletions

View File

@ -160,9 +160,16 @@ func (p *AttachOptions) Run() error {
return fmt.Errorf("pod %s is not running and cannot be attached to; current phase is %s", p.PodName, pod.Status.Phase)
}
// TODO: refactor with terminal helpers from the edit utility once that is merged
var stdin io.Reader
tty := p.TTY
containerToAttach := p.GetContainer(pod)
if tty && !containerToAttach.TTY {
tty = false
fmt.Fprintf(p.Err, "Unable to use a TTY - container %s doesn't allocate one\n", containerToAttach.Name)
}
// TODO: refactor with terminal helpers from the edit utility once that is merged
if p.Stdin {
stdin = p.In
if tty {
@ -204,7 +211,7 @@ func (p *AttachOptions) Run() error {
Namespace(pod.Namespace).
SubResource("attach")
req.VersionedParams(&api.PodAttachOptions{
Container: p.GetContainerName(pod),
Container: containerToAttach.Name,
Stdin: stdin != nil,
Stdout: p.Out != nil,
Stderr: p.Err != nil,
@ -214,12 +221,21 @@ func (p *AttachOptions) Run() error {
return p.Attach.Attach("POST", req.URL(), p.Config, stdin, p.Out, p.Err, tty)
}
// GetContainerName returns the name of the container to attach to, with a fallback.
func (p *AttachOptions) GetContainerName(pod *api.Pod) string {
// GetContainer returns the container to attach to, with a fallback.
func (p *AttachOptions) GetContainer(pod *api.Pod) api.Container {
if len(p.ContainerName) > 0 {
return p.ContainerName
for _, container := range pod.Spec.Containers {
if container.Name == p.ContainerName {
return container
}
}
}
glog.V(4).Infof("defaulting container name to %s", pod.Spec.Containers[0].Name)
return pod.Spec.Containers[0].Name
return pod.Spec.Containers[0]
}
// GetContainerName returns the name of the container to attach to, with a fallback.
func (p *AttachOptions) GetContainerName(pod *api.Pod) string {
return p.GetContainer(pod).Name
}

View File

@ -22,6 +22,7 @@ import (
"io"
"net/http"
"net/url"
"strings"
"testing"
"github.com/spf13/cobra"
@ -192,6 +193,72 @@ func TestAttach(t *testing.T) {
}
}
func TestAttachWarnings(t *testing.T) {
version := testapi.Default.Version()
tests := []struct {
name, container, version, podPath, expectedErr, expectedOut string
pod *api.Pod
stdin, tty bool
}{
{
name: "fallback tty if not supported",
version: version,
podPath: "/api/" + version + "/namespaces/test/pods/foo",
pod: attachPod(),
stdin: true,
tty: true,
expectedErr: "Unable to use a TTY - container bar doesn't allocate one",
},
}
for _, test := range tests {
f, tf, codec := NewAPIFactory()
tf.Client = &fake.RESTClient{
Codec: codec,
Client: fake.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == test.podPath && m == "GET":
body := objBody(codec, test.pod)
return &http.Response{StatusCode: 200, Body: body}, nil
default:
t.Errorf("%s: unexpected request: %s %#v\n%#v", test.name, req.Method, req.URL, req)
return nil, fmt.Errorf("unexpected request")
}
}),
}
tf.Namespace = "test"
tf.ClientConfig = &client.Config{Version: test.version}
bufOut := bytes.NewBuffer([]byte{})
bufErr := bytes.NewBuffer([]byte{})
bufIn := bytes.NewBuffer([]byte{})
ex := &fakeRemoteAttach{}
params := &AttachOptions{
ContainerName: test.container,
In: bufIn,
Out: bufOut,
Err: bufErr,
Stdin: test.stdin,
TTY: test.tty,
Attach: ex,
}
cmd := &cobra.Command{}
if err := params.Complete(f, cmd, []string{"foo"}); err != nil {
t.Fatal(err)
}
if err := params.Run(); err != nil {
t.Fatal(err)
}
if test.stdin && test.tty {
if !test.pod.Spec.Containers[0].TTY {
if !strings.Contains(bufErr.String(), test.expectedErr) {
t.Errorf("%s: Expected TTY fallback warning for attach request: %s", test.name, bufErr.String())
continue
}
}
}
}
}
func attachPod() *api.Pod {
return &api.Pod{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},