Skip to content

Commit

Permalink
virtcontainers: add pause and resume container to the API
Browse files Browse the repository at this point in the history
Pause and resume container functions allow us to just pause/resume a
specific container not all the sanbox, in that way different containers
can be paused or running in the same sanbox.

Signed-off-by: Julio Montes <julio.montes@intel.com>
  • Loading branch information
Julio Montes committed May 31, 2018
1 parent 44b65e1 commit b99cadb
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 7 deletions.
6 changes: 6 additions & 0 deletions virtcontainers/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,10 @@ type agent interface {

// statsContainer will tell the agent to get stats from a container related to a Sandbox
statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error)

// pauseContainer will pause a container
pauseContainer(sandbox *Sandbox, c Container) error

// resumeContainer will resume a paused container
resumeContainer(sandbox *Sandbox, c Container) error
}
43 changes: 43 additions & 0 deletions virtcontainers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,3 +653,46 @@ func StatsContainer(sandboxID, containerID string) (ContainerStats, error) {

return s.StatsContainer(containerID)
}

func togglePauseContainer(sandboxID, containerID string, pause bool) error {
if sandboxID == "" {
return errNeedSandboxID
}

if containerID == "" {
return errNeedContainerID
}

lockFile, err := rwLockSandbox(sandboxID)
if err != nil {
return err
}
defer unlockSandbox(lockFile)

s, err := fetchSandbox(sandboxID)
if err != nil {
return err
}

// Fetch the container.
c, err := s.findContainer(containerID)
if err != nil {
return err
}

if pause {
return c.pause()
}

return c.resume()
}

// PauseContainer is the virtcontainers container pause entry point.
func PauseContainer(sandboxID, containerID string) error {
return togglePauseContainer(sandboxID, containerID, true)
}

// ResumeContainer is the virtcontainers container resume entry point.
func ResumeContainer(sandboxID, containerID string) error {
return togglePauseContainer(sandboxID, containerID, false)
}
40 changes: 40 additions & 0 deletions virtcontainers/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2400,3 +2400,43 @@ func TestUpdateContainer(t *testing.T) {
err = UpdateContainer(s.ID(), contID, resources)
assert.NoError(err)
}

func TestPauseResumeContainer(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip(testDisabledAsNonRoot)
}

cleanUp()

assert := assert.New(t)
err := PauseContainer("", "")
assert.Error(err)

err = PauseContainer("abc", "")
assert.Error(err)

contID := "100"
config := newTestSandboxConfigNoop()

s, sandboxDir, err := createAndStartSandbox(config)
assert.NoError(err)
assert.NotNil(s)

contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(s.ID(), contConfig)
assert.NoError(err)
assert.NotNil(c)

contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
assert.NoError(err)

_, err = StartContainer(s.ID(), contID)
assert.NoError(err)

err = PauseContainer(s.ID(), contID)
assert.NoError(err)

err = ResumeContainer(s.ID(), contID)
assert.NoError(err)
}
36 changes: 34 additions & 2 deletions virtcontainers/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -868,8 +868,8 @@ func (c *Container) signalProcess(processID string, signal syscall.Signal, all b
return fmt.Errorf("Sandbox not ready or running, impossible to signal the container")
}

if c.state.State != StateReady && c.state.State != StateRunning {
return fmt.Errorf("Container not ready or running, impossible to signal the container")
if c.state.State != StateReady && c.state.State != StateRunning && c.state.State != StatePaused {
return fmt.Errorf("Container not ready, running or paused, impossible to signal the container")
}

return c.sandbox.agent.signalProcess(c, processID, signal, all)
Expand Down Expand Up @@ -938,6 +938,38 @@ func (c *Container) update(resources specs.LinuxResources) error {
return c.sandbox.agent.updateContainer(c.sandbox, *c, resources)
}

func (c *Container) pause() error {
if err := c.checkSandboxRunning("pause"); err != nil {
return err
}

if c.state.State != StateRunning && c.state.State != StateReady {
return fmt.Errorf("Container not running or ready, impossible to pause")
}

if err := c.sandbox.agent.pauseContainer(c.sandbox, *c); err != nil {
return err
}

return c.setContainerState(StatePaused)
}

func (c *Container) resume() error {
if err := c.checkSandboxRunning("resume"); err != nil {
return err
}

if c.state.State != StatePaused {
return fmt.Errorf("Container not paused, impossible to resume")
}

if err := c.sandbox.agent.resumeContainer(c.sandbox, *c); err != nil {
return err
}

return c.setContainerState(StateRunning)
}

func (c *Container) hotplugDrive() error {
dev, err := getDeviceForPath(c.rootFs)

Expand Down
5 changes: 0 additions & 5 deletions virtcontainers/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,6 @@ func TestKillContainerErrorState(t *testing.T) {
err := c.kill(syscall.SIGKILL, true)
assert.Error(err)

// Container paused
c.state.State = StatePaused
err = c.kill(syscall.SIGKILL, false)
assert.Error(err)

// Container stopped
c.state.State = StateStopped
err = c.kill(syscall.SIGKILL, true)
Expand Down
10 changes: 10 additions & 0 deletions virtcontainers/hyperstart_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,13 @@ func (h *hyper) readProcessStderr(c *Container, processID string, data []byte) (
// hyperstart-agent does not support stderr read request
return 0, nil
}

func (h *hyper) pauseContainer(sandbox *Sandbox, c Container) error {
// hyperstart-agent does not support pause container
return nil
}

func (h *hyper) resumeContainer(sandbox *Sandbox, c Container) error {
// hyperstart-agent does not support resume container
return nil
}
10 changes: 10 additions & 0 deletions virtcontainers/implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,13 @@ func (impl *VCImpl) ProcessListContainer(sandboxID, containerID string, options
func (impl *VCImpl) UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error {
return UpdateContainer(sandboxID, containerID, resources)
}

// PauseContainer implements the VC function of the same name.
func (impl *VCImpl) PauseContainer(sandboxID, containerID string) error {
return PauseContainer(sandboxID, containerID)
}

// ResumeContainer implements the VC function of the same name.
func (impl *VCImpl) ResumeContainer(sandboxID, containerID string) error {
return ResumeContainer(sandboxID, containerID)
}
2 changes: 2 additions & 0 deletions virtcontainers/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type VC interface {
StopContainer(sandboxID, containerID string) (VCContainer, error)
ProcessListContainer(sandboxID, containerID string, options ProcessListOptions) (ProcessList, error)
UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error
PauseContainer(sandboxID, containerID string) error
ResumeContainer(sandboxID, containerID string) error
}

// VCSandbox is the Sandbox interface
Expand Down
24 changes: 24 additions & 0 deletions virtcontainers/kata_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,24 @@ func (k *kataAgent) updateContainer(sandbox *Sandbox, c Container, resources spe
return err
}

func (k *kataAgent) pauseContainer(sandbox *Sandbox, c Container) error {
req := &grpc.PauseContainerRequest{
ContainerId: c.id,
}

_, err := k.sendReq(req)
return err
}

func (k *kataAgent) resumeContainer(sandbox *Sandbox, c Container) error {
req := &grpc.ResumeContainerRequest{
ContainerId: c.id,
}

_, err := k.sendReq(req)
return err
}

func (k *kataAgent) onlineCPUMem(cpus uint32) error {
req := &grpc.OnlineCPUMemRequest{
Wait: false,
Expand Down Expand Up @@ -1155,6 +1173,12 @@ func (k *kataAgent) installReqFunc(c *kataclient.AgentClient) {
k.reqHandlers["grpc.StatsContainerRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.StatsContainer(ctx, req.(*grpc.StatsContainerRequest), opts...)
}
k.reqHandlers["grpc.PauseContainerRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.PauseContainer(ctx, req.(*grpc.PauseContainerRequest), opts...)
}
k.reqHandlers["grpc.ResumeContainerRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
return k.client.ResumeContainer(ctx, req.(*grpc.ResumeContainerRequest), opts...)
}
}

func (k *kataAgent) sendReq(request interface{}) (interface{}, error) {
Expand Down
10 changes: 10 additions & 0 deletions virtcontainers/noop_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,13 @@ func (n *noopAgent) readProcessStdout(c *Container, processID string, data []byt
func (n *noopAgent) readProcessStderr(c *Container, processID string, data []byte) (int, error) {
return 0, nil
}

// pauseContainer is the Noop agent Container pause implementation. It does nothing.
func (n *noopAgent) pauseContainer(sandbox *Sandbox, c Container) error {
return nil
}

// resumeContainer is the Noop agent Container resume implementation. It does nothing.
func (n *noopAgent) resumeContainer(sandbox *Sandbox, c Container) error {
return nil
}
26 changes: 26 additions & 0 deletions virtcontainers/noop_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,29 @@ func TestNoopAgentStatsContainer(t *testing.T) {
t.Fatal(err)
}
}

func TestNoopAgentPauseContainer(t *testing.T) {
n := &noopAgent{}
sandbox, container, err := testCreateNoopContainer()
if err != nil {
t.Fatal(err)
}
defer cleanUp()
err = n.pauseContainer(sandbox, *container)
if err != nil {
t.Fatal(err)
}
}

func TestNoopAgentResumeContainer(t *testing.T) {
n := &noopAgent{}
sandbox, container, err := testCreateNoopContainer()
if err != nil {
t.Fatal(err)
}
defer cleanUp()
err = n.resumeContainer(sandbox, *container)
if err != nil {
t.Fatal(err)
}
}
5 changes: 5 additions & 0 deletions virtcontainers/pkg/oci/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const (

// StateStopped represents a container that has been stopped.
StateStopped = "stopped"

// StatePaused represents a container that has been paused.
StatePaused = "paused"
)

// CompatOCIProcess is a structure inheriting from spec.Process defined
Expand Down Expand Up @@ -612,6 +615,8 @@ func StateToOCIState(state vc.State) string {
return StateRunning
case vc.StateStopped:
return StateStopped
case vc.StatePaused:
return StatePaused
default:
return ""
}
Expand Down
5 changes: 5 additions & 0 deletions virtcontainers/pkg/oci/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,11 @@ func TestStateToOCIState(t *testing.T) {
if ociState := StateToOCIState(state); ociState != "stopped" {
t.Fatalf("Expecting \"created\" state, got \"%s\"", ociState)
}

state.State = vc.StatePaused
if ociState := StateToOCIState(state); ociState != "paused" {
t.Fatalf("Expecting \"paused\" state, got \"%s\"", ociState)
}
}

func TestEnvVars(t *testing.T) {
Expand Down
18 changes: 18 additions & 0 deletions virtcontainers/pkg/vcmock/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,21 @@ func (m *VCMock) UpdateContainer(sandboxID, containerID string, resources specs.

return fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
}

// PauseContainer implements the VC function of the same name.
func (m *VCMock) PauseContainer(sandboxID, containerID string) error {
if m.PauseContainerFunc != nil {
return m.PauseContainerFunc(sandboxID, containerID)
}

return fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
}

// ResumeContainer implements the VC function of the same name.
func (m *VCMock) ResumeContainer(sandboxID, containerID string) error {
if m.ResumeContainerFunc != nil {
return m.ResumeContainerFunc(sandboxID, containerID)
}

return fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
}
52 changes: 52 additions & 0 deletions virtcontainers/pkg/vcmock/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,3 +623,55 @@ func TestVCMockFetchSandbox(t *testing.T) {
assert.True(IsMockError(err))

}

func TestVCMockPauseContainer(t *testing.T) {
assert := assert.New(t)

m := &VCMock{}
config := &vc.SandboxConfig{}
assert.Nil(m.PauseContainerFunc)

err := m.PauseContainer(config.ID, config.ID)
assert.Error(err)
assert.True(IsMockError(err))

m.PauseContainerFunc = func(sid, cid string) error {
return nil
}

err = m.PauseContainer(config.ID, config.ID)
assert.NoError(err)

// reset
m.PauseContainerFunc = nil

err = m.PauseContainer(config.ID, config.ID)
assert.Error(err)
assert.True(IsMockError(err))
}

func TestVCMockResumeContainer(t *testing.T) {
assert := assert.New(t)

m := &VCMock{}
config := &vc.SandboxConfig{}
assert.Nil(m.ResumeContainerFunc)

err := m.ResumeContainer(config.ID, config.ID)
assert.Error(err)
assert.True(IsMockError(err))

m.ResumeContainerFunc = func(sid, cid string) error {
return nil
}

err = m.ResumeContainer(config.ID, config.ID)
assert.NoError(err)

// reset
m.ResumeContainerFunc = nil

err = m.ResumeContainer(config.ID, config.ID)
assert.Error(err)
assert.True(IsMockError(err))
}
2 changes: 2 additions & 0 deletions virtcontainers/pkg/vcmock/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ type VCMock struct {
StopContainerFunc func(sandboxID, containerID string) (vc.VCContainer, error)
ProcessListContainerFunc func(sandboxID, containerID string, options vc.ProcessListOptions) (vc.ProcessList, error)
UpdateContainerFunc func(sandboxID, containerID string, resources specs.LinuxResources) error
PauseContainerFunc func(sandboxID, containerID string) error
ResumeContainerFunc func(sandboxID, containerID string) error
}

0 comments on commit b99cadb

Please sign in to comment.