
This also fixes a deadlock in the shim's reaper where execs would lockup and/or miss a quick exiting exec process's exit status. Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
292 lines
5.5 KiB
Go
292 lines
5.5 KiB
Go
package containerd
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"syscall"
|
|
"testing"
|
|
)
|
|
|
|
func empty() IOCreation {
|
|
return BufferedIO(bytes.NewBuffer(nil), bytes.NewBuffer(nil), bytes.NewBuffer(nil))
|
|
}
|
|
|
|
func TestContainerList(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
client, err := New(address)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
containers, err := client.Containers(context.Background())
|
|
if err != nil {
|
|
t.Errorf("container list returned error %v", err)
|
|
return
|
|
}
|
|
if len(containers) != 0 {
|
|
t.Errorf("expected 0 containers but received %d", len(containers))
|
|
}
|
|
}
|
|
|
|
func TestNewContainer(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
id := "NewContainer"
|
|
client, err := New(address)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
spec, err := GenerateSpec()
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
container, err := client.NewContainer(context.Background(), id, spec)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer container.Delete(context.Background())
|
|
if container.ID() != id {
|
|
t.Errorf("expected container id %q but received %q", id, container.ID())
|
|
}
|
|
if spec, err = container.Spec(); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
if err := container.Delete(context.Background()); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func TestContainerStart(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
client, err := New(address)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
var (
|
|
ctx = context.Background()
|
|
id = "ContainerStart"
|
|
)
|
|
image, err := client.GetImage(ctx, testImage)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sh", "-c", "exit 7"))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
container, err := client.NewContainer(ctx, id, spec, WithImage(image), WithNewRootFS(id, image))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer container.Delete(ctx)
|
|
|
|
task, err := container.NewTask(ctx, Stdio)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer task.Delete(ctx)
|
|
|
|
statusC := make(chan uint32, 1)
|
|
go func() {
|
|
status, err := task.Wait(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
statusC <- status
|
|
}()
|
|
|
|
if pid := task.Pid(); pid <= 0 {
|
|
t.Errorf("invalid task pid %d", pid)
|
|
}
|
|
if err := task.Start(ctx); err != nil {
|
|
t.Error(err)
|
|
task.Delete(ctx)
|
|
return
|
|
}
|
|
status := <-statusC
|
|
if status != 7 {
|
|
t.Errorf("expected status 7 from wait but received %d", status)
|
|
}
|
|
if status, err = task.Delete(ctx); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
if status != 7 {
|
|
t.Errorf("expected status 7 from delete but received %d", status)
|
|
}
|
|
}
|
|
|
|
func TestContainerOutput(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
client, err := New(address)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
var (
|
|
ctx = context.Background()
|
|
id = "ContainerOutput"
|
|
expected = "kingkoye"
|
|
)
|
|
image, err := client.GetImage(ctx, testImage)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("echo", expected))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
container, err := client.NewContainer(ctx, id, spec, WithImage(image), WithNewRootFS(id, image))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer container.Delete(ctx)
|
|
|
|
stdout := bytes.NewBuffer(nil)
|
|
task, err := container.NewTask(ctx, BufferedIO(bytes.NewBuffer(nil), stdout, bytes.NewBuffer(nil)))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer task.Delete(ctx)
|
|
|
|
statusC := make(chan uint32, 1)
|
|
go func() {
|
|
status, err := task.Wait(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
statusC <- status
|
|
}()
|
|
|
|
if err := task.Start(ctx); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
status := <-statusC
|
|
if status != 0 {
|
|
t.Errorf("expected status 0 but received %d", status)
|
|
}
|
|
if _, err := task.Delete(ctx); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
actual := stdout.String()
|
|
// echo adds a new line
|
|
expected = expected + "\n"
|
|
if actual != expected {
|
|
t.Errorf("expected output %q but received %q", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestContainerExec(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip()
|
|
}
|
|
client, err := New(address)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer client.Close()
|
|
|
|
var (
|
|
ctx = context.Background()
|
|
id = "ContainerExec"
|
|
)
|
|
image, err := client.GetImage(ctx, testImage)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
container, err := client.NewContainer(ctx, id, spec, WithImage(image), WithNewRootFS(id, image))
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer container.Delete(ctx)
|
|
|
|
task, err := container.NewTask(ctx, empty())
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer task.Delete(ctx)
|
|
|
|
finished := make(chan struct{}, 1)
|
|
go func() {
|
|
if _, err := task.Wait(ctx); err != nil {
|
|
t.Error(err)
|
|
}
|
|
close(finished)
|
|
}()
|
|
|
|
// start an exec process without running the original container process info
|
|
processSpec := spec.Process
|
|
processSpec.Args = []string{
|
|
"sh", "-c",
|
|
"exit 6",
|
|
}
|
|
|
|
process, err := task.Exec(ctx, &processSpec, empty())
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
processStatusC := make(chan uint32, 1)
|
|
go func() {
|
|
status, err := process.Wait(ctx)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
processStatusC <- status
|
|
}()
|
|
|
|
if err := process.Start(ctx); err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
|
|
// wait for the exec to return
|
|
status := <-processStatusC
|
|
|
|
if status != 6 {
|
|
t.Errorf("expected exec exit code 6 but received %d", status)
|
|
}
|
|
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
|
|
t.Error(err)
|
|
}
|
|
<-finished
|
|
}
|