mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-29 16:57:18 +00:00
virtcontainers: add pause and resume container to the API
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>
This commit is contained in:
parent
44b65e1d52
commit
b99cadb553
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
@ -612,6 +615,8 @@ func StateToOCIState(state vc.State) string {
|
||||
return StateRunning
|
||||
case vc.StateStopped:
|
||||
return StateStopped
|
||||
case vc.StatePaused:
|
||||
return StatePaused
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user