From bf4ef4324e0a6722257ebbdd33e680630764b8d8 Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Wed, 25 Apr 2018 22:22:06 +0800 Subject: [PATCH] API: add sandbox winsizeprocess api It sends tty resize request to the agent to resize a process's tty window. Signed-off-by: Peng Tao --- virtcontainers/agent.go | 3 +++ virtcontainers/container.go | 8 ++++++++ virtcontainers/container_test.go | 26 ++++++++++++++++++++++++ virtcontainers/hyperstart_agent.go | 5 +++++ virtcontainers/interfaces.go | 1 + virtcontainers/kata_agent.go | 15 ++++++++++++++ virtcontainers/noop_agent.go | 5 +++++ virtcontainers/pkg/vcmock/sandbox.go | 5 +++++ virtcontainers/sandbox.go | 14 +++++++++++++ virtcontainers/sandbox_test.go | 30 ++++++++++++++++++++++++++++ 10 files changed, 112 insertions(+) diff --git a/virtcontainers/agent.go b/virtcontainers/agent.go index fe0c84a49..f785ec603 100644 --- a/virtcontainers/agent.go +++ b/virtcontainers/agent.go @@ -165,6 +165,9 @@ type agent interface { // the container will be sent the signal. signalProcess(c *Container, processID string, signal syscall.Signal, all bool) error + // winsizeProcess will tell the agent to set a process' tty size + winsizeProcess(c *Container, processID string, height, width uint32) error + // processListContainer will list the processes running inside the container processListContainer(sandbox *Sandbox, c Container, options ProcessListOptions) (ProcessList, error) diff --git a/virtcontainers/container.go b/virtcontainers/container.go index 9a62082bb..11d6fdb37 100644 --- a/virtcontainers/container.go +++ b/virtcontainers/container.go @@ -750,6 +750,14 @@ func (c *Container) signalProcess(processID string, signal syscall.Signal, all b return c.sandbox.agent.signalProcess(c, processID, signal, all) } +func (c *Container) winsizeProcess(processID string, height, width uint32) error { + if c.state.State != StateReady && c.state.State != StateRunning { + return fmt.Errorf("Container not ready or running, impossible to signal the container") + } + + return c.sandbox.agent.winsizeProcess(c, processID, height, width) +} + func (c *Container) processList(options ProcessListOptions) (ProcessList, error) { if err := c.checkSandboxRunning("ps"); err != nil { return nil, err diff --git a/virtcontainers/container_test.go b/virtcontainers/container_test.go index 8b23e1b7c..79303807a 100644 --- a/virtcontainers/container_test.go +++ b/virtcontainers/container_test.go @@ -409,3 +409,29 @@ func TestKillContainerErrorState(t *testing.T) { err = c.kill(syscall.SIGKILL, true) assert.Error(err) } + +func TestWinsizeProcessErrorState(t *testing.T) { + assert := assert.New(t) + c := &Container{ + sandbox: &Sandbox{ + state: State{ + State: StateRunning, + }, + }, + } + processID := "foobar" + + // Container state undefined + err := c.winsizeProcess(processID, 100, 200) + assert.Error(err) + + // Container paused + c.state.State = StatePaused + err = c.winsizeProcess(processID, 100, 200) + assert.Error(err) + + // Container stopped + c.state.State = StateStopped + err = c.winsizeProcess(processID, 100, 200) + assert.Error(err) +} diff --git a/virtcontainers/hyperstart_agent.go b/virtcontainers/hyperstart_agent.go index a5ab42319..cbc03b476 100644 --- a/virtcontainers/hyperstart_agent.go +++ b/virtcontainers/hyperstart_agent.go @@ -811,3 +811,8 @@ func (h *hyper) waitProcess(c *Container, processID string) (int32, error) { // cc-agent does not support wait process return 0, nil } + +func (h *hyper) winsizeProcess(c *Container, processID string, height, width uint32) error { + // cc-agent does not support winsize process + return nil +} diff --git a/virtcontainers/interfaces.go b/virtcontainers/interfaces.go index 0968620b9..f1dcc9d7a 100644 --- a/virtcontainers/interfaces.go +++ b/virtcontainers/interfaces.go @@ -59,6 +59,7 @@ type VCSandbox interface { EnterContainer(containerID string, cmd Cmd) (VCContainer, *Process, error) WaitProcess(containerID, processID string) (int32, error) SignalProcess(containerID, processID string, signal syscall.Signal, all bool) error + WinsizeProcess(containerID, processID string, height, width uint32) error } // VCContainer is the Container interface diff --git a/virtcontainers/kata_agent.go b/virtcontainers/kata_agent.go index 55486dca5..fc60a6906 100644 --- a/virtcontainers/kata_agent.go +++ b/virtcontainers/kata_agent.go @@ -876,6 +876,18 @@ func (k *kataAgent) signalProcess(c *Container, processID string, signal syscall return err } +func (k *kataAgent) winsizeProcess(c *Container, processID string, height, width uint32) error { + req := &grpc.TtyWinResizeRequest{ + ContainerId: c.id, + ExecId: processID, + Row: height, + Column: width, + } + + _, err := k.sendReq(req) + return err +} + func (k *kataAgent) processListContainer(sandbox *Sandbox, c Container, options ProcessListOptions) (ProcessList, error) { req := &grpc.ListProcessesRequest{ ContainerId: c.id, @@ -999,6 +1011,9 @@ func (k *kataAgent) installReqFunc(c *kataclient.AgentClient) { k.reqHandlers["grpc.WaitProcessRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { return k.client.WaitProcess(ctx, req.(*grpc.WaitProcessRequest), opts...) } + k.reqHandlers["grpc.TtyWinResizeRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) { + return k.client.TtyWinResize(ctx, req.(*grpc.TtyWinResizeRequest), opts...) + } } func (k *kataAgent) sendReq(request interface{}) (interface{}, error) { diff --git a/virtcontainers/noop_agent.go b/virtcontainers/noop_agent.go index 4f92040ae..08ee17ccf 100644 --- a/virtcontainers/noop_agent.go +++ b/virtcontainers/noop_agent.go @@ -88,3 +88,8 @@ func (n *noopAgent) check() error { func (n *noopAgent) waitProcess(c *Container, processID string) (int32, error) { return 0, nil } + +// winsizeProcess is the Noop agent process tty resizer. It does nothing. +func (n *noopAgent) winsizeProcess(c *Container, processID string, height, width uint32) error { + return nil +} diff --git a/virtcontainers/pkg/vcmock/sandbox.go b/virtcontainers/pkg/vcmock/sandbox.go index 913f93bcc..c100c829b 100644 --- a/virtcontainers/pkg/vcmock/sandbox.go +++ b/virtcontainers/pkg/vcmock/sandbox.go @@ -116,3 +116,8 @@ func (p *Sandbox) WaitProcess(containerID, processID string) (int32, error) { func (p *Sandbox) SignalProcess(containerID, processID string, signal syscall.Signal, all bool) error { return nil } + +// WinsizeProcess implements the VCSandbox function of the same name. +func (p *Sandbox) WinsizeProcess(containerID, processID string, height, width uint32) error { + return nil +} diff --git a/virtcontainers/sandbox.go b/virtcontainers/sandbox.go index 1afcbd334..4dc0ebee8 100644 --- a/virtcontainers/sandbox.go +++ b/virtcontainers/sandbox.go @@ -616,6 +616,20 @@ func (s *Sandbox) SignalProcess(containerID, processID string, signal syscall.Si return c.signalProcess(processID, signal, all) } +// WinsizeProcess resizes the tty window of a process +func (s *Sandbox) WinsizeProcess(containerID, processID string, height, width uint32) error { + if s.state.State != StateRunning { + return fmt.Errorf("Sandbox not running") + } + + c, err := s.findContainer(containerID) + if err != nil { + return err + } + + return c.winsizeProcess(processID, height, width) +} + func createAssets(sandboxConfig *SandboxConfig) error { kernel, err := newAsset(sandboxConfig, kernelAsset) if err != nil { diff --git a/virtcontainers/sandbox_test.go b/virtcontainers/sandbox_test.go index 0d6adc4c1..67e7ddd03 100644 --- a/virtcontainers/sandbox_test.go +++ b/virtcontainers/sandbox_test.go @@ -1469,3 +1469,33 @@ func TestSignalProcess(t *testing.T) { err = s.SignalProcess(contID, execID, syscall.SIGKILL, false) assert.Nil(t, err, "Wait process failed: %v", err) } + +func TestWinsizeProcess(t *testing.T) { + s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NoopAgentType, NoopNetworkModel, NetworkConfig{}, nil, nil) + assert.Nil(t, err, "VirtContainers should not allow empty sandboxes") + defer cleanUp() + + contID := "foo" + execID := "bar" + err = s.WinsizeProcess(contID, execID, 100, 200) + assert.NotNil(t, err, "Winsize process in stopped sandbox should fail") + + err = s.start() + assert.Nil(t, err, "Failed to start sandbox: %v", err) + + err = s.WinsizeProcess(contID, execID, 100, 200) + assert.NotNil(t, err, "Winsize process in non-existing container should fail") + + contConfig := newTestContainerConfigNoop(contID) + _, err = s.CreateContainer(contConfig) + assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err) + + err = s.WinsizeProcess(contID, execID, 100, 200) + assert.Nil(t, err, "Winsize process in ready container failed: %v", err) + + _, err = s.StartContainer(contID) + assert.Nil(t, err, "Start container failed: %v", err) + + err = s.WinsizeProcess(contID, execID, 100, 200) + assert.Nil(t, err, "Winsize process failed: %v", err) +}