Commit 5960761b authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #36615 from timstclair/cri-attach-tty

Automatic merge from submit-queue [CRI] Add TTY flag to AttachRequest Follow up from https://github.com/kubernetes/kubernetes/pull/35661 For https://github.com/kubernetes/kubernetes/issues/29579 - Add TTY to the CRI AttachRequest - Moves responsibility from the runtime shim to the Kubelet for populating the TTY bool in the request based on the container spec /cc @euank @feiskyer @kubernetes/sig-node
parents fed53f3b cc801e97
...@@ -719,7 +719,7 @@ message ExecRequest { ...@@ -719,7 +719,7 @@ message ExecRequest {
optional string container_id = 1; optional string container_id = 1;
// Command to execute. // Command to execute.
repeated string cmd = 2; repeated string cmd = 2;
// Whether use tty. // Whether to exec the command in a TTY.
optional bool tty = 3; optional bool tty = 3;
// Whether to stream stdin. // Whether to stream stdin.
optional bool stdin = 4; optional bool stdin = 4;
...@@ -735,6 +735,9 @@ message AttachRequest { ...@@ -735,6 +735,9 @@ message AttachRequest {
optional string container_id = 1; optional string container_id = 1;
// Whether to stream stdin. // Whether to stream stdin.
optional bool stdin = 2; optional bool stdin = 2;
// Whether the process being attached is running in a TTY.
// This must match the TTY setting in the ContainerConfig.
optional bool tty = 3;
} }
message AttachResponse { message AttachResponse {
......
...@@ -246,3 +246,18 @@ func (r *containerCommandRunnerWrapper) RunInContainer(id ContainerID, cmd []str ...@@ -246,3 +246,18 @@ func (r *containerCommandRunnerWrapper) RunInContainer(id ContainerID, cmd []str
// error. // error.
return buffer.Bytes(), err return buffer.Bytes(), err
} }
// GetContainerSpec gets the container spec by containerName.
func GetContainerSpec(pod *v1.Pod, containerName string) *v1.Container {
for i, c := range pod.Spec.Containers {
if containerName == c.Name {
return &pod.Spec.Containers[i]
}
}
for i, c := range pod.Spec.InitContainers {
if containerName == c.Name {
return &pod.Spec.InitContainers[i]
}
}
return nil
}
...@@ -140,7 +140,7 @@ type DirectStreamingRuntime interface { ...@@ -140,7 +140,7 @@ type DirectStreamingRuntime interface {
// the runtime server. // the runtime server.
type IndirectStreamingRuntime interface { type IndirectStreamingRuntime interface {
GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error) GetExec(id ContainerID, cmd []string, stdin, stdout, stderr, tty bool) (*url.URL, error)
GetAttach(id ContainerID, stdin, stdout, stderr bool) (*url.URL, error) GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error)
GetPortForward(podName, podNamespace string, podUID types.UID) (*url.URL, error) GetPortForward(podName, podNamespace string, podUID types.UID) (*url.URL, error)
} }
......
...@@ -78,12 +78,16 @@ type FakeDirectStreamingRuntime struct { ...@@ -78,12 +78,16 @@ type FakeDirectStreamingRuntime struct {
} }
} }
var _ DirectStreamingRuntime = &FakeDirectStreamingRuntime{}
const FakeHost = "localhost:12345" const FakeHost = "localhost:12345"
type FakeIndirectStreamingRuntime struct { type FakeIndirectStreamingRuntime struct {
*FakeRuntime *FakeRuntime
} }
var _ IndirectStreamingRuntime = &FakeIndirectStreamingRuntime{}
// FakeRuntime should implement Runtime. // FakeRuntime should implement Runtime.
var _ Runtime = &FakeRuntime{} var _ Runtime = &FakeRuntime{}
...@@ -459,7 +463,7 @@ func (f *FakeIndirectStreamingRuntime) GetExec(id ContainerID, cmd []string, std ...@@ -459,7 +463,7 @@ func (f *FakeIndirectStreamingRuntime) GetExec(id ContainerID, cmd []string, std
return &url.URL{Host: FakeHost}, f.Err return &url.URL{Host: FakeHost}, f.Err
} }
func (f *FakeIndirectStreamingRuntime) GetAttach(id ContainerID, stdin, stdout, stderr bool) (*url.URL, error) { func (f *FakeIndirectStreamingRuntime) GetAttach(id ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) {
f.Lock() f.Lock()
defer f.Unlock() defer f.Unlock()
......
...@@ -51,13 +51,12 @@ func (r *streamingRuntime) exec(containerID string, cmd []string, in io.Reader, ...@@ -51,13 +51,12 @@ func (r *streamingRuntime) exec(containerID string, cmd []string, in io.Reader,
return r.execHandler.ExecInContainer(r.client, container, cmd, in, out, errw, tty, resize, timeout) return r.execHandler.ExecInContainer(r.client, container, cmd, in, out, errw, tty, resize, timeout)
} }
func (r *streamingRuntime) Attach(containerID string, in io.Reader, out, errw io.WriteCloser, resize <-chan term.Size) error { func (r *streamingRuntime) Attach(containerID string, in io.Reader, out, errw io.WriteCloser, tty bool, resize <-chan term.Size) error {
container, err := checkContainerStatus(r.client, containerID) _, err := checkContainerStatus(r.client, containerID)
if err != nil { if err != nil {
return err return err
} }
tty := container.Config.Tty
return dockertools.AttachContainer(r.client, containerID, in, out, errw, tty, resize) return dockertools.AttachContainer(r.client, containerID, in, out, errw, tty, resize)
} }
...@@ -99,12 +98,11 @@ func (ds *dockerService) Attach(req *runtimeapi.AttachRequest) (*runtimeapi.Atta ...@@ -99,12 +98,11 @@ func (ds *dockerService) Attach(req *runtimeapi.AttachRequest) (*runtimeapi.Atta
if ds.streamingServer == nil { if ds.streamingServer == nil {
return nil, streaming.ErrorStreamingDisabled("attach") return nil, streaming.ErrorStreamingDisabled("attach")
} }
container, err := checkContainerStatus(ds.client, req.GetContainerId()) _, err := checkContainerStatus(ds.client, req.GetContainerId())
if err != nil { if err != nil {
return nil, err return nil, err
} }
tty := container.Config.Tty return ds.streamingServer.GetAttach(req)
return ds.streamingServer.GetAttach(req, tty)
} }
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address. // PortForward prepares a streaming endpoint to forward ports from a PodSandbox, and returns the address.
......
...@@ -1350,10 +1350,23 @@ func (kl *Kubelet) GetAttach(podFullName string, podUID types.UID, containerName ...@@ -1350,10 +1350,23 @@ func (kl *Kubelet) GetAttach(podFullName string, podUID types.UID, containerName
return nil, err return nil, err
} }
if container == nil { if container == nil {
return nil, fmt.Errorf("container not found (%q)", containerName) return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName)
}
// The TTY setting for attach must match the TTY setting in the initial container configuration,
// since whether the process is running in a TTY cannot be changed after it has started. We
// need the api.Pod to get the TTY status.
pod, found := kl.GetPodByFullName(podFullName)
if !found || pod.UID != podUID {
return nil, fmt.Errorf("pod %s not found", podFullName)
}
containerSpec := kubecontainer.GetContainerSpec(pod, containerName)
if containerSpec == nil {
return nil, fmt.Errorf("container %s not found in pod %s", containerName, podFullName)
} }
tty := containerSpec.TTY
return streamingRuntime.GetAttach(container.ID, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr) return streamingRuntime.GetAttach(container.ID, streamOpts.Stdin, streamOpts.Stdout, streamOpts.Stderr, tty)
default: default:
return nil, fmt.Errorf("container runtime does not support attach") return nil, fmt.Errorf("container runtime does not support attach")
} }
......
...@@ -130,22 +130,6 @@ func (m *kubeGenericRuntimeManager) sandboxToKubeContainer(s *runtimeapi.PodSand ...@@ -130,22 +130,6 @@ func (m *kubeGenericRuntimeManager) sandboxToKubeContainer(s *runtimeapi.PodSand
}, nil }, nil
} }
// getContainerSpec gets the container spec by containerName.
func getContainerSpec(pod *v1.Pod, containerName string) *v1.Container {
for i, c := range pod.Spec.Containers {
if containerName == c.Name {
return &pod.Spec.Containers[i]
}
}
for i, c := range pod.Spec.InitContainers {
if containerName == c.Name {
return &pod.Spec.InitContainers[i]
}
}
return nil
}
// getImageUser gets uid or user name that will run the command(s) from image. The function // getImageUser gets uid or user name that will run the command(s) from image. The function
// guarantees that only one of them is set. // guarantees that only one of them is set.
func (m *kubeGenericRuntimeManager) getImageUser(image string) (*int64, *string, error) { func (m *kubeGenericRuntimeManager) getImageUser(image string) (*int64, *string, error) {
......
...@@ -490,7 +490,7 @@ func (m *kubeGenericRuntimeManager) restoreSpecsFromContainerLabels(containerID ...@@ -490,7 +490,7 @@ func (m *kubeGenericRuntimeManager) restoreSpecsFromContainerLabels(containerID
func (m *kubeGenericRuntimeManager) killContainer(pod *v1.Pod, containerID kubecontainer.ContainerID, containerName string, reason string, gracePeriodOverride *int64) error { func (m *kubeGenericRuntimeManager) killContainer(pod *v1.Pod, containerID kubecontainer.ContainerID, containerName string, reason string, gracePeriodOverride *int64) error {
var containerSpec *v1.Container var containerSpec *v1.Container
if pod != nil { if pod != nil {
containerSpec = getContainerSpec(pod, containerName) containerSpec = kubecontainer.GetContainerSpec(pod, containerName)
} else { } else {
// Restore necessary information if one of the specs is nil. // Restore necessary information if one of the specs is nil.
restoredPod, restoredContainer, err := m.restoreSpecsFromContainerLabels(containerID) restoredPod, restoredContainer, err := m.restoreSpecsFromContainerLabels(containerID)
...@@ -684,10 +684,11 @@ func (m *kubeGenericRuntimeManager) GetExec(id kubecontainer.ContainerID, cmd [] ...@@ -684,10 +684,11 @@ func (m *kubeGenericRuntimeManager) GetExec(id kubecontainer.ContainerID, cmd []
} }
// GetAttach gets the endpoint the runtime will serve the attach request from. // GetAttach gets the endpoint the runtime will serve the attach request from.
func (m *kubeGenericRuntimeManager) GetAttach(id kubecontainer.ContainerID, stdin, stdout, stderr bool) (*url.URL, error) { func (m *kubeGenericRuntimeManager) GetAttach(id kubecontainer.ContainerID, stdin, stdout, stderr, tty bool) (*url.URL, error) {
req := &runtimeapi.AttachRequest{ req := &runtimeapi.AttachRequest{
ContainerId: &id.ID, ContainerId: &id.ID,
Stdin: &stdin, Stdin: &stdin,
Tty: &tty,
} }
resp, err := m.runtimeService.Attach(req) resp, err := m.runtimeService.Attach(req)
if err != nil { if err != nil {
......
...@@ -42,7 +42,7 @@ type Server interface { ...@@ -42,7 +42,7 @@ type Server interface {
// Get the serving URL for the requests. // Get the serving URL for the requests.
// Requests must not be nil. Responses may be nil iff an error is returned. // Requests must not be nil. Responses may be nil iff an error is returned.
GetExec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error) GetExec(*runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, error)
GetAttach(req *runtimeapi.AttachRequest, tty bool) (*runtimeapi.AttachResponse, error) GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error)
GetPortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error) GetPortForward(*runtimeapi.PortForwardRequest) (*runtimeapi.PortForwardResponse, error)
// Start the server. // Start the server.
...@@ -58,7 +58,7 @@ type Server interface { ...@@ -58,7 +58,7 @@ type Server interface {
// The interface to execute the commands and provide the streams. // The interface to execute the commands and provide the streams.
type Runtime interface { type Runtime interface {
Exec(containerID string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error Exec(containerID string, cmd []string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error
Attach(containerID string, in io.Reader, out, err io.WriteCloser, resize <-chan term.Size) error Attach(containerID string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error
PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error
} }
...@@ -154,12 +154,12 @@ func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse, ...@@ -154,12 +154,12 @@ func (s *server) GetExec(req *runtimeapi.ExecRequest) (*runtimeapi.ExecResponse,
}, nil }, nil
} }
func (s *server) GetAttach(req *runtimeapi.AttachRequest, tty bool) (*runtimeapi.AttachResponse, error) { func (s *server) GetAttach(req *runtimeapi.AttachRequest) (*runtimeapi.AttachResponse, error) {
url := s.buildURL("attach", req.GetContainerId(), streamOpts{ url := s.buildURL("attach", req.GetContainerId(), streamOpts{
stdin: req.GetStdin(), stdin: req.GetStdin(),
stdout: true, stdout: true,
stderr: !tty, // For TTY connections, both stderr is combined with stdout. stderr: !req.GetTty(), // For TTY connections, both stderr is combined with stdout.
tty: tty, tty: req.GetTty(),
}) })
return &runtimeapi.AttachResponse{ return &runtimeapi.AttachResponse{
Url: &url, Url: &url,
...@@ -314,7 +314,7 @@ func (a *criAdapter) ExecInContainer(podName string, podUID types.UID, container ...@@ -314,7 +314,7 @@ func (a *criAdapter) ExecInContainer(podName string, podUID types.UID, container
} }
func (a *criAdapter) AttachContainer(podName string, podUID types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error { func (a *criAdapter) AttachContainer(podName string, podUID types.UID, container string, in io.Reader, out, err io.WriteCloser, tty bool, resize <-chan term.Size) error {
return a.Attach(container, in, out, err, resize) return a.Attach(container, in, out, err, tty, resize)
} }
func (a *criAdapter) PortForward(podName string, podUID types.UID, port uint16, stream io.ReadWriteCloser) error { func (a *criAdapter) PortForward(podName string, podUID types.UID, port uint16, stream io.ReadWriteCloser) error {
......
...@@ -134,15 +134,16 @@ func TestGetAttach(t *testing.T) { ...@@ -134,15 +134,16 @@ func TestGetAttach(t *testing.T) {
request := &runtimeapi.AttachRequest{ request := &runtimeapi.AttachRequest{
ContainerId: &containerID, ContainerId: &containerID,
Stdin: &test.stdin, Stdin: &test.stdin,
Tty: &test.tty,
} }
// Non-TLS // Non-TLS
resp, err := server.GetAttach(request, test.tty) resp, err := server.GetAttach(request)
assert.NoError(t, err, "testcase=%+v", test) assert.NoError(t, err, "testcase=%+v", test)
expectedURL := "http://" + testAddr + "/attach/" + testContainerID + test.expectedQuery expectedURL := "http://" + testAddr + "/attach/" + testContainerID + test.expectedQuery
assert.Equal(t, expectedURL, resp.GetUrl(), "testcase=%+v", test) assert.Equal(t, expectedURL, resp.GetUrl(), "testcase=%+v", test)
// TLS // TLS
resp, err = tlsServer.GetAttach(request, test.tty) resp, err = tlsServer.GetAttach(request)
assert.NoError(t, err, "testcase=%+v", test) assert.NoError(t, err, "testcase=%+v", test)
expectedURL = "https://" + testAddr + "/attach/" + testContainerID + test.expectedQuery expectedURL = "https://" + testAddr + "/attach/" + testContainerID + test.expectedQuery
assert.Equal(t, expectedURL, resp.GetUrl(), "testcase=%+v", test) assert.Equal(t, expectedURL, resp.GetUrl(), "testcase=%+v", test)
...@@ -299,7 +300,7 @@ func (f *fakeRuntime) Exec(containerID string, cmd []string, stdin io.Reader, st ...@@ -299,7 +300,7 @@ func (f *fakeRuntime) Exec(containerID string, cmd []string, stdin io.Reader, st
return nil return nil
} }
func (f *fakeRuntime) Attach(containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, resize <-chan term.Size) error { func (f *fakeRuntime) Attach(containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) error {
assert.Equal(f.t, testContainerID, containerID) assert.Equal(f.t, testContainerID, containerID)
doServerStreams(f.t, "attach", stdin, stdout, stderr) doServerStreams(f.t, "attach", stdin, stdout, stderr)
return nil return nil
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment