Skip to content

Commit 4219f7b

Browse files
Merge pull request #2307 from avagin/tty
Allow to checkpoint and restore a container with console
2 parents cddd791 + 29c76b1 commit 4219f7b

7 files changed

Lines changed: 163 additions & 28 deletions

File tree

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ install:
4646
- sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-runc
4747
- sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-cni
4848
- sudo PATH=$PATH GOPATH=$GOPATH script/setup/install-critools
49-
- wget https://github.com/xemul/criu/archive/v3.0.tar.gz -O /tmp/criu.tar.gz
49+
- wget https://github.com/checkpoint-restore/criu/archive/v3.7.tar.gz -O /tmp/criu.tar.gz
5050
- tar -C /tmp/ -zxf /tmp/criu.tar.gz
51-
- cd /tmp/criu-3.0 && sudo make install-criu
51+
- cd /tmp/criu-3.7 && sudo make install-criu
5252
- cd $TRAVIS_BUILD_DIR
5353

5454
script:

cmd/ctr/commands/tasks/tasks_unix.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,24 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
6969
// NewTask creates a new task
7070
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
7171
stdio := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...)
72-
if checkpoint == "" {
73-
ioCreator := stdio
74-
if tty {
75-
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
76-
}
77-
if nullIO {
78-
if tty {
79-
return nil, errors.New("tty and null-io cannot be used together")
80-
}
81-
ioCreator = cio.NullIO
72+
if checkpoint != "" {
73+
im, err := client.GetImage(ctx, checkpoint)
74+
if err != nil {
75+
return nil, err
8276
}
83-
return container.NewTask(ctx, ioCreator, opts...)
77+
opts = append(opts, containerd.WithTaskCheckpoint(im))
8478
}
85-
im, err := client.GetImage(ctx, checkpoint)
86-
if err != nil {
87-
return nil, err
79+
ioCreator := stdio
80+
if tty {
81+
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
82+
}
83+
if nullIO {
84+
if tty {
85+
return nil, errors.New("tty and null-io cannot be used together")
86+
}
87+
ioCreator = cio.NullIO
8888
}
89-
opts = append(opts, containerd.WithTaskCheckpoint(im))
90-
return container.NewTask(ctx, stdio, opts...)
89+
return container.NewTask(ctx, ioCreator, opts...)
9190
}
9291

9392
func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {

container_checkpoint_test.go

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build !windows
1+
// +build linux
22

33
/*
44
Copyright The containerd Authors.
@@ -19,12 +19,122 @@
1919
package containerd
2020

2121
import (
22+
"bytes"
23+
"fmt"
24+
"io"
25+
"strings"
26+
"sync"
2227
"syscall"
2328
"testing"
2429

2530
"github.com/containerd/containerd/oci"
2631
)
2732

33+
func TestCheckpointRestorePTY(t *testing.T) {
34+
if !supportsCriu {
35+
t.Skip("system does not have criu installed")
36+
}
37+
client, err := newClient(t, address)
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
defer client.Close()
42+
43+
var (
44+
ctx, cancel = testContext()
45+
id = t.Name()
46+
)
47+
defer cancel()
48+
49+
image, err := client.GetImage(ctx, testImage)
50+
if err != nil {
51+
t.Fatal(err)
52+
}
53+
container, err := client.NewContainer(ctx, id,
54+
WithNewSpec(oci.WithImageConfig(image),
55+
oci.WithProcessArgs("sh", "-c", "read A; echo z${A}z"),
56+
oci.WithTTY),
57+
WithNewSnapshot(id, image))
58+
if err != nil {
59+
t.Fatal(err)
60+
}
61+
defer container.Delete(ctx, WithSnapshotCleanup)
62+
63+
direct, err := newDirectIOWithTerminal(ctx)
64+
if err != nil {
65+
t.Fatal(err)
66+
}
67+
defer direct.Delete()
68+
69+
task, err := container.NewTask(ctx, direct.IOCreate)
70+
if err != nil {
71+
t.Fatal(err)
72+
}
73+
defer task.Delete(ctx)
74+
75+
statusC, err := task.Wait(ctx)
76+
if err != nil {
77+
t.Fatal(err)
78+
}
79+
80+
if err := task.Start(ctx); err != nil {
81+
t.Fatal(err)
82+
}
83+
84+
checkpoint, err := task.Checkpoint(ctx, WithExit)
85+
if err != nil {
86+
t.Fatal(err)
87+
}
88+
89+
<-statusC
90+
91+
if _, err := task.Delete(ctx); err != nil {
92+
t.Fatal(err)
93+
}
94+
direct.Delete()
95+
direct, err = newDirectIOWithTerminal(ctx)
96+
if err != nil {
97+
t.Fatal(err)
98+
}
99+
100+
var (
101+
wg sync.WaitGroup
102+
buf = bytes.NewBuffer(nil)
103+
)
104+
wg.Add(1)
105+
go func() {
106+
defer wg.Done()
107+
io.Copy(buf, direct.Stdout)
108+
}()
109+
110+
if task, err = container.NewTask(ctx, direct.IOCreate,
111+
WithTaskCheckpoint(checkpoint)); err != nil {
112+
t.Fatal(err)
113+
}
114+
115+
statusC, err = task.Wait(ctx)
116+
if err != nil {
117+
t.Fatal(err)
118+
}
119+
120+
if err := task.Start(ctx); err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
direct.Stdin.Write([]byte("hello\n"))
125+
<-statusC
126+
wg.Wait()
127+
128+
if err := direct.Close(); err != nil {
129+
t.Error(err)
130+
}
131+
132+
out := buf.String()
133+
if !strings.Contains(fmt.Sprintf("%#q", out), `zhelloz`) {
134+
t.Fatalf(`expected \x00 in output: %s`, out)
135+
}
136+
}
137+
28138
func TestCheckpointRestore(t *testing.T) {
29139
if !supportsCriu {
30140
t.Skip("system does not have criu installed")

linux/proc/init_state.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,23 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
194194
s.p.mu.Lock()
195195
defer s.p.mu.Unlock()
196196
p := s.p
197+
sio := p.stdio
198+
199+
var (
200+
err error
201+
socket *runc.Socket
202+
)
203+
if sio.Terminal {
204+
if socket, err = runc.NewTempConsoleSocket(); err != nil {
205+
return errors.Wrap(err, "failed to create OCI runtime console socket")
206+
}
207+
defer socket.Close()
208+
s.opts.ConsoleSocket = socket
209+
}
210+
197211
if _, err := s.p.runtime.Restore(ctx, p.id, p.bundle, s.opts); err != nil {
198212
return p.runtimeError(err, "OCI runtime restore failed")
199213
}
200-
sio := p.stdio
201214
if sio.Stdin != "" {
202215
sc, err := fifo.OpenFifo(ctx, sio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
203216
if err != nil {
@@ -207,7 +220,17 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
207220
p.closers = append(p.closers, sc)
208221
}
209222
var copyWaitGroup sync.WaitGroup
210-
if !sio.IsNull() {
223+
if socket != nil {
224+
console, err := socket.ReceiveMaster()
225+
if err != nil {
226+
return errors.Wrap(err, "failed to retrieve console master")
227+
}
228+
console, err = p.platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup)
229+
if err != nil {
230+
return errors.Wrap(err, "failed to start console copy")
231+
}
232+
p.console = console
233+
} else if !sio.IsNull() {
211234
if err := copyPipes(ctx, p.io, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup); err != nil {
212235
return errors.Wrap(err, "failed to start io pipe copy")
213236
}
@@ -219,7 +242,6 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
219242
return errors.Wrap(err, "failed to retrieve OCI runtime container pid")
220243
}
221244
p.pid = pid
222-
223245
return s.transition("running")
224246
}
225247

vendor.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
github.com/containerd/go-runc bcb223a061a3dd7de1a89c0b402a60f4dd9bd307
1+
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5
22
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
33
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
44
github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788

vendor/github.com/containerd/go-runc/console.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/containerd/go-runc/runc.go

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)