Update integration test to support windows
Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
parent
79d04ded4e
commit
651aaff74e
@ -20,7 +20,7 @@ func BenchmarkContainerCreate(b *testing.B) {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("true"))
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
@ -63,7 +63,7 @@ func BenchmarkContainerStart(b *testing.B) {
|
||||
b.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("true"))
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
return
|
||||
|
@ -1,3 +1,5 @@
|
||||
// +build !windows
|
||||
|
||||
package containerd
|
||||
|
||||
import (
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@ -17,11 +18,6 @@ import (
|
||||
"github.com/containerd/containerd/testutil"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultRoot = "/var/lib/containerd-test"
|
||||
testImage = "docker.io/library/alpine:latest"
|
||||
)
|
||||
|
||||
var (
|
||||
address string
|
||||
noDaemon bool
|
||||
@ -29,7 +25,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&address, "address", "/run/containerd-test/containerd.sock", "The address to the containerd socket for use in the tests")
|
||||
flag.StringVar(&address, "address", defaultAddress, "The address to the containerd socket for use in the tests")
|
||||
flag.BoolVar(&noDaemon, "no-daemon", false, "Do not start a dedicated daemon for the tests")
|
||||
flag.Parse()
|
||||
}
|
||||
@ -57,11 +53,15 @@ func TestMain(m *testing.M) {
|
||||
defer cancel()
|
||||
|
||||
if !noDaemon {
|
||||
os.RemoveAll(defaultRoot)
|
||||
|
||||
// setup a new containerd daemon if !testing.Short
|
||||
cmd = exec.Command("containerd",
|
||||
"--root", defaultRoot,
|
||||
"--address", address,
|
||||
"--log-level", "debug",
|
||||
)
|
||||
cmd.Stdout = buf
|
||||
cmd.Stderr = buf
|
||||
if err := cmd.Start(); err != nil {
|
||||
cmd.Wait()
|
||||
@ -94,14 +94,22 @@ func TestMain(m *testing.M) {
|
||||
}).Info("running tests against containerd")
|
||||
|
||||
// pull a seed image
|
||||
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
cmd.Wait()
|
||||
fmt.Fprintf(os.Stderr, "%s: %s", err, buf.String())
|
||||
if runtime.GOOS != "windows" { // TODO: remove once pull is supported on windows
|
||||
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
|
||||
cmd.Process.Signal(syscall.SIGTERM)
|
||||
cmd.Wait()
|
||||
fmt.Fprintf(os.Stderr, "%s: %s", err, buf.String())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := platformTestSetup(client); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "platform test setup failed", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := client.Close(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "failed to close client", err)
|
||||
}
|
||||
|
||||
// run the test
|
||||
@ -110,13 +118,15 @@ func TestMain(m *testing.M) {
|
||||
if !noDaemon {
|
||||
// tear down the daemon and resources created
|
||||
if err := cmd.Process.Signal(syscall.SIGTERM); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to signal containerd", err)
|
||||
}
|
||||
}
|
||||
if err := cmd.Wait(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "failed to wait for containerd", err)
|
||||
}
|
||||
if err := os.RemoveAll(defaultRoot); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr, "failed to remove test root dir", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// only print containerd logs if the test failed
|
||||
@ -171,6 +181,11 @@ func TestNewClient(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestImagePull(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// TODO: remove once Windows has a snapshotter
|
||||
t.Skip("Windows does not have a snapshotter yet")
|
||||
}
|
||||
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
13
client_unix_test.go
Normal file
13
client_unix_test.go
Normal file
@ -0,0 +1,13 @@
|
||||
// +build !windows
|
||||
|
||||
package containerd
|
||||
|
||||
const (
|
||||
defaultRoot = "/var/lib/containerd-test"
|
||||
defaultAddress = "/run/containerd-test/containerd.sock"
|
||||
testImage = "docker.io/library/alpine:latest"
|
||||
)
|
||||
|
||||
func platformTestSetup(client *Client) error {
|
||||
return nil
|
||||
}
|
87
client_windows_test.go
Normal file
87
client_windows_test.go
Normal file
@ -0,0 +1,87 @@
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultAddress = `\\.\pipe\containerd-containerd-test`
|
||||
testImage = "docker.io/library/go:nanoserver"
|
||||
)
|
||||
|
||||
var (
|
||||
dockerLayerFolders []string
|
||||
|
||||
defaultRoot = filepath.Join(os.Getenv("programfiles"), "containerd", "root-test")
|
||||
)
|
||||
|
||||
func platformTestSetup(client *Client) error {
|
||||
var (
|
||||
roots []string
|
||||
layerChains = make(map[string]string)
|
||||
)
|
||||
// Since we can't pull images yet, we'll piggyback on the default
|
||||
// docker's images
|
||||
wfPath := `C:\ProgramData\docker\windowsfilter`
|
||||
wf, err := os.Open(wfPath)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to access docker layers @ %s", wfPath)
|
||||
}
|
||||
defer wf.Close()
|
||||
entries, err := wf.Readdirnames(0)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read %s entries", wfPath)
|
||||
}
|
||||
|
||||
for _, fn := range entries {
|
||||
layerChainPath := filepath.Join(wfPath, fn, "layerchain.json")
|
||||
lfi, err := os.Stat(layerChainPath)
|
||||
switch {
|
||||
case err == nil && lfi.Mode().IsRegular():
|
||||
f, err := os.OpenFile(layerChainPath, os.O_RDONLY, 0660)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr,
|
||||
errors.Wrapf(err, "failed to open %s", layerChainPath))
|
||||
continue
|
||||
}
|
||||
defer f.Close()
|
||||
l := make([]string, 0)
|
||||
if err := json.NewDecoder(f).Decode(&l); err != nil {
|
||||
fmt.Fprintln(os.Stderr,
|
||||
errors.Wrapf(err, "failed to decode %s", layerChainPath))
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case len(l) == 1:
|
||||
layerChains[l[0]] = filepath.Join(wfPath, fn)
|
||||
case len(l) > 1:
|
||||
fmt.Fprintf(os.Stderr, "Too many entries in %s: %d", layerChainPath, len(l))
|
||||
case len(l) == 0:
|
||||
roots = append(roots, filepath.Join(wfPath, fn))
|
||||
}
|
||||
case os.IsNotExist(err):
|
||||
// keep on going
|
||||
default:
|
||||
return errors.Wrapf(err, "error trying to access %s", layerChainPath)
|
||||
}
|
||||
}
|
||||
|
||||
// They'll be 2 roots, just take the first one
|
||||
l := roots[0]
|
||||
dockerLayerFolders = append(dockerLayerFolders, l)
|
||||
for {
|
||||
l = layerChains[l]
|
||||
if l == "" {
|
||||
break
|
||||
}
|
||||
|
||||
dockerLayerFolders = append([]string{l}, dockerLayerFolders...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
100
container_linux_test.go
Normal file
100
container_linux_test.go
Normal file
@ -0,0 +1,100 @@
|
||||
// +build linux
|
||||
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/cgroups"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func TestContainerUpdate(t *testing.T) {
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := generateSpec(WithImageConfig(ctx, image), withProcessArgs("sleep", "30"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
limit := int64(32 * 1024 * 1024)
|
||||
spec.Linux.Resources.Memory = &specs.LinuxMemory{
|
||||
Limit: &limit,
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer container.Delete(ctx, WithRootFSDeletion)
|
||||
|
||||
task, err := container.NewTask(ctx, empty())
|
||||
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
|
||||
}()
|
||||
|
||||
// check that the task has a limit of 32mb
|
||||
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if int64(stat.Memory.Usage.Limit) != limit {
|
||||
t.Errorf("expected memory limit to be set to %d but received %d", limit, stat.Memory.Usage.Limit)
|
||||
return
|
||||
}
|
||||
limit = 64 * 1024 * 1024
|
||||
if err := task.Update(ctx, WithResources(&specs.LinuxResources{
|
||||
Memory: &specs.LinuxMemory{
|
||||
Limit: &limit,
|
||||
},
|
||||
})); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// check that the task has a limit of 64mb
|
||||
if stat, err = cgroup.Stat(cgroups.IgnoreNotExist); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if int64(stat.Memory.Usage.Limit) != limit {
|
||||
t.Errorf("expected memory limit to be set to %d but received %d", limit, stat.Memory.Usage.Limit)
|
||||
}
|
||||
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
<-statusC
|
||||
}
|
@ -6,13 +6,16 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/cgroups"
|
||||
// Register the typeurl
|
||||
_ "github.com/containerd/containerd/runtime"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
func empty() IOCreation {
|
||||
@ -48,7 +51,7 @@ func TestNewContainer(t *testing.T) {
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
spec, err := GenerateSpec()
|
||||
spec, err := generateSpec()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -84,22 +87,26 @@ func TestContainerStart(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
image, err = client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spec, err := generateSpec(withImageConfig(ctx, image), withExitStatus(7))
|
||||
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, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -151,23 +158,27 @@ func TestContainerOutput(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
expected = "kingkoye"
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
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
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("echo", expected))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -207,7 +218,7 @@ func TestContainerOutput(t *testing.T) {
|
||||
|
||||
actual := stdout.String()
|
||||
// echo adds a new line
|
||||
expected = expected + "\n"
|
||||
expected = expected + newLine
|
||||
if actual != expected {
|
||||
t.Errorf("expected output %q but received %q", expected, actual)
|
||||
}
|
||||
@ -221,22 +232,26 @@ func TestContainerExec(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
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
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -260,10 +275,7 @@ func TestContainerExec(t *testing.T) {
|
||||
|
||||
// start an exec process without running the original container process info
|
||||
processSpec := spec.Process
|
||||
processSpec.Args = []string{
|
||||
"sh", "-c",
|
||||
"exit 6",
|
||||
}
|
||||
withExecExitStatus(processSpec, 6)
|
||||
execID := t.Name() + "_exec"
|
||||
process, err := task.Exec(ctx, execID, processSpec, empty())
|
||||
if err != nil {
|
||||
@ -305,7 +317,7 @@ func TestContainerExec(t *testing.T) {
|
||||
<-finished
|
||||
}
|
||||
|
||||
func TestContainerProcesses(t *testing.T) {
|
||||
func TestContainerPids(t *testing.T) {
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -313,22 +325,26 @@ func TestContainerProcesses(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
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
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -383,29 +399,33 @@ func TestContainerCloseIO(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
image, err = client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spec, err := generateSpec(withImageConfig(ctx, image), withCat())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("cat"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer container.Delete(ctx, WithRootFSDeletion)
|
||||
|
||||
const expected = "hello\n"
|
||||
const expected = "hello" + newLine
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
|
||||
r, w, err := os.Pipe()
|
||||
@ -451,12 +471,25 @@ func TestContainerCloseIO(t *testing.T) {
|
||||
|
||||
output := stdout.String()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
// On windows we use more and it always adds an extra newline
|
||||
// remove it here
|
||||
output = strings.TrimSuffix(output, newLine)
|
||||
}
|
||||
|
||||
if output != expected {
|
||||
t.Errorf("expected output %q but received %q", expected, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerAttach(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// On windows, closing the write side of the pipe closes the read
|
||||
// side, sending an EOF to it and preventing reopening it.
|
||||
// Hence this test will always fails on windows
|
||||
t.Skip("invalid logic on windows")
|
||||
}
|
||||
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -464,29 +497,33 @@ func TestContainerAttach(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
image, err = client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spec, err := generateSpec(withImageConfig(ctx, image), withCat())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("cat"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer container.Delete(ctx, WithRootFSDeletion)
|
||||
|
||||
expected := "hello\n"
|
||||
expected := "hello" + newLine
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
|
||||
r, w, err := os.Pipe()
|
||||
@ -586,22 +623,26 @@ func TestDeleteRunningContainer(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
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
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithImage(image), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -651,22 +692,26 @@ func TestContainerKill(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
image, err = client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spec, err := generateSpec(withImageConfig(ctx, image), withCat())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sh", "-c", "cat"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithImage(image), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
@ -709,95 +754,6 @@ func TestContainerKill(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerUpdate(t *testing.T) {
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "30"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
limit := int64(32 * 1024 * 1024)
|
||||
spec.Linux.Resources.Memory = &specs.LinuxMemory{
|
||||
Limit: &limit,
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer container.Delete(ctx, WithRootFSDeletion)
|
||||
|
||||
task, err := container.NewTask(ctx, empty())
|
||||
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
|
||||
}()
|
||||
|
||||
// check that the task has a limit of 32mb
|
||||
cgroup, err := cgroups.Load(cgroups.V1, cgroups.PidPath(int(task.Pid())))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
stat, err := cgroup.Stat(cgroups.IgnoreNotExist)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if int64(stat.Memory.Usage.Limit) != limit {
|
||||
t.Errorf("expected memory limit to be set to %d but received %d", limit, stat.Memory.Usage.Limit)
|
||||
return
|
||||
}
|
||||
limit = 64 * 1024 * 1024
|
||||
if err := task.Update(ctx, WithResources(&specs.LinuxResources{
|
||||
Memory: &specs.LinuxMemory{
|
||||
Limit: &limit,
|
||||
},
|
||||
})); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// check that the task has a limit of 64mb
|
||||
if stat, err = cgroup.Stat(cgroups.IgnoreNotExist); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
if int64(stat.Memory.Usage.Limit) != limit {
|
||||
t.Errorf("expected memory limit to be set to %d but received %d", limit, stat.Memory.Usage.Limit)
|
||||
}
|
||||
if err := task.Kill(ctx, syscall.SIGKILL); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
<-statusC
|
||||
}
|
||||
|
||||
func TestContainerNoBinaryExists(t *testing.T) {
|
||||
client, err := newClient(t, address)
|
||||
if err != nil {
|
||||
@ -806,30 +762,47 @@ func TestContainerNoBinaryExists(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
image, err = client.GetImage(ctx, testImage)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spec, err := generateSpec(withImageConfig(ctx, image), withProcessArgs("nothing"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("nothing"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
defer container.Delete(ctx, WithRootFSDeletion)
|
||||
|
||||
if _, err := container.NewTask(ctx, Stdio); err == nil {
|
||||
t.Error("NewTask should return an error when binary does not exist")
|
||||
task, err := container.NewTask(ctx, Stdio)
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
if err != nil {
|
||||
t.Errorf("failed to create task %v", err)
|
||||
}
|
||||
if err := task.Start(ctx); err != nil {
|
||||
t.Error("task.Start() should return an error when binary does not exist")
|
||||
task.Delete(ctx)
|
||||
}
|
||||
default:
|
||||
if err == nil {
|
||||
t.Error("NewTask should return an error when binary does not exist")
|
||||
task.Delete(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -841,22 +814,26 @@ func TestContainerExecNoBinaryExists(t *testing.T) {
|
||||
defer client.Close()
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext()
|
||||
id = t.Name()
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
image, err := client.GetImage(ctx, testImage)
|
||||
if runtime.GOOS != "windows" {
|
||||
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
|
||||
}
|
||||
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
|
||||
container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
|
@ -258,9 +258,11 @@ func checkBlobPath(t *testing.T, cs Store, dgst digest.Digest) string {
|
||||
t.Fatalf("error stating blob path: %v", err)
|
||||
}
|
||||
|
||||
// ensure that only read bits are set.
|
||||
if ((fi.Mode() & os.ModePerm) & 0333) != 0 {
|
||||
t.Fatalf("incorrect permissions: %v", fi.Mode())
|
||||
if runtime.GOOS != "windows" {
|
||||
// ensure that only read bits are set.
|
||||
if ((fi.Mode() & os.ModePerm) & 0333) != 0 {
|
||||
t.Fatalf("incorrect permissions: %v", fi.Mode())
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
|
@ -3,6 +3,7 @@ package content
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
@ -67,8 +68,12 @@ func (w *writer) Commit(size int64, expected digest.Digest) error {
|
||||
// only allowing reads honoring the umask on creation.
|
||||
//
|
||||
// This removes write and exec, only allowing read per the creation umask.
|
||||
if err := w.fp.Chmod((fi.Mode() & os.ModePerm) &^ 0333); err != nil {
|
||||
return errors.Wrap(err, "failed to change ingest file permissions")
|
||||
//
|
||||
// NOTE: Windows does not support this operation
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := w.fp.Chmod((fi.Mode() & os.ModePerm) &^ 0333); err != nil {
|
||||
return errors.Wrap(err, "failed to change ingest file permissions")
|
||||
}
|
||||
}
|
||||
|
||||
if size > 0 && size != fi.Size() {
|
||||
|
51
helpers_unix_test.go
Normal file
51
helpers_unix_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
// +build !windows
|
||||
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
const newLine = "\n"
|
||||
|
||||
func generateSpec(opts ...SpecOpts) (*specs.Spec, error) {
|
||||
return GenerateSpec(opts...)
|
||||
}
|
||||
|
||||
func withExitStatus(es int) SpecOpts {
|
||||
return func(s *specs.Spec) error {
|
||||
s.Process.Args = []string{"sh", "-c", fmt.Sprintf("exit %d", es)}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func withProcessArgs(args ...string) SpecOpts {
|
||||
return WithProcessArgs(args...)
|
||||
}
|
||||
|
||||
func withCat() SpecOpts {
|
||||
return WithProcessArgs("cat")
|
||||
}
|
||||
|
||||
func withTrue() SpecOpts {
|
||||
return WithProcessArgs("true")
|
||||
}
|
||||
|
||||
func withExecExitStatus(s *specs.Process, es int) {
|
||||
s.Args = []string{"sh", "-c", fmt.Sprintf("exit %d", es)}
|
||||
}
|
||||
|
||||
func withExecArgs(s *specs.Process, args ...string) {
|
||||
s.Args = args
|
||||
}
|
||||
|
||||
func withImageConfig(ctx context.Context, i Image) SpecOpts {
|
||||
return WithImageConfig(ctx, i)
|
||||
}
|
||||
|
||||
func withNewRootFS(id string, i Image) NewContainerOpts {
|
||||
return WithNewRootFS(id, i)
|
||||
}
|
65
helpers_windows_test.go
Normal file
65
helpers_windows_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
// +build windows
|
||||
|
||||
package containerd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/containerd/containers"
|
||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||
)
|
||||
|
||||
const newLine = "\r\n"
|
||||
|
||||
func generateSpec(opts ...SpecOpts) (*specs.Spec, error) {
|
||||
spec, err := GenerateSpec(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec.Windows.LayerFolders = dockerLayerFolders
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func withExitStatus(es int) SpecOpts {
|
||||
return func(s *specs.Spec) error {
|
||||
s.Process.Args = []string{"powershell", "-noprofile", "exit", strconv.Itoa(es)}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func withProcessArgs(args ...string) SpecOpts {
|
||||
return WithProcessArgs(append([]string{"powershell", "-noprofile"}, args...)...)
|
||||
}
|
||||
|
||||
func withCat() SpecOpts {
|
||||
return WithProcessArgs("cmd", "/c", "more")
|
||||
}
|
||||
|
||||
func withTrue() SpecOpts {
|
||||
return WithProcessArgs("cmd", "/c")
|
||||
}
|
||||
|
||||
func withExecExitStatus(s *specs.Process, es int) {
|
||||
s.Args = []string{"powershell", "-noprofile", "exit", strconv.Itoa(es)}
|
||||
}
|
||||
|
||||
func withExecArgs(s *specs.Process, args ...string) {
|
||||
s.Args = append([]string{"powershell", "-noprofile"}, args...)
|
||||
}
|
||||
|
||||
func withImageConfig(ctx context.Context, i Image) SpecOpts {
|
||||
// TODO: when windows has a snapshotter remove the withImageConfig helper
|
||||
return func(s *specs.Spec) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func withNewRootFS(id string, i Image) NewContainerOpts {
|
||||
// TODO: when windows has a snapshotter remove the withNewRootFS helper
|
||||
return func(ctx context.Context, client *Client, c *containers.Container) error {
|
||||
return nil
|
||||
}
|
||||
}
|
@ -2,15 +2,11 @@ package testutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var rootEnabled bool
|
||||
@ -19,36 +15,6 @@ func init() {
|
||||
flag.BoolVar(&rootEnabled, "test.root", false, "enable tests that require root")
|
||||
}
|
||||
|
||||
// Unmount unmounts a given mountPoint and sets t.Error if it fails
|
||||
func Unmount(t *testing.T, mountPoint string) {
|
||||
t.Log("unmount", mountPoint)
|
||||
if err := mount.Unmount(mountPoint, 0); err != nil {
|
||||
t.Error("Could not umount", mountPoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RequiresRoot skips tests that require root, unless the test.root flag has
|
||||
// been set
|
||||
func RequiresRoot(t testing.TB) {
|
||||
if !rootEnabled {
|
||||
t.Skip("skipping test that requires root")
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 0, os.Getuid(), "This test must be run as root.")
|
||||
}
|
||||
|
||||
// RequiresRootM is similar to RequiresRoot but intended to be called from *testing.M.
|
||||
func RequiresRootM() {
|
||||
if !rootEnabled {
|
||||
fmt.Fprintln(os.Stderr, "skipping test that requires root")
|
||||
os.Exit(0)
|
||||
}
|
||||
if 0 != os.Getuid() {
|
||||
fmt.Fprintln(os.Stderr, "This test must be run as root.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// DumpDir will log out all of the contents of the provided directory to
|
||||
// testing logger.
|
||||
//
|
||||
|
42
testutil/helpers_unix.go
Normal file
42
testutil/helpers_unix.go
Normal file
@ -0,0 +1,42 @@
|
||||
// +build !windows
|
||||
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Unmount unmounts a given mountPoint and sets t.Error if it fails
|
||||
func Unmount(t *testing.T, mountPoint string) {
|
||||
t.Log("unmount", mountPoint)
|
||||
if err := mount.Unmount(mountPoint, 0); err != nil {
|
||||
t.Error("Could not umount", mountPoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RequiresRoot skips tests that require root, unless the test.root flag has
|
||||
// been set
|
||||
func RequiresRoot(t testing.TB) {
|
||||
if !rootEnabled {
|
||||
t.Skip("skipping test that requires root")
|
||||
return
|
||||
}
|
||||
assert.Equal(t, 0, os.Getuid(), "This test must be run as root.")
|
||||
}
|
||||
|
||||
// RequiresRootM is similar to RequiresRoot but intended to be called from *testing.M.
|
||||
func RequiresRootM() {
|
||||
if !rootEnabled {
|
||||
fmt.Fprintln(os.Stderr, "skipping test that requires root")
|
||||
os.Exit(0)
|
||||
}
|
||||
if 0 != os.Getuid() {
|
||||
fmt.Fprintln(os.Stderr, "This test must be run as root.")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
16
testutil/helpers_windows.go
Normal file
16
testutil/helpers_windows.go
Normal file
@ -0,0 +1,16 @@
|
||||
package testutil
|
||||
|
||||
import "testing"
|
||||
|
||||
// RequiresRoot does nothing on Windows
|
||||
func RequiresRoot(t testing.TB) {
|
||||
}
|
||||
|
||||
// RequiresRootM is similar to RequiresRoot but intended to be called from *testing.M.
|
||||
func RequiresRootM() {
|
||||
}
|
||||
|
||||
// Unmount unmounts a given mountPoint and sets t.Error if it fails
|
||||
// Does nothing on Windows
|
||||
func Unmount(t *testing.T, mountPoint string) {
|
||||
}
|
Loading…
Reference in New Issue
Block a user