Update integration test to support windows

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
This commit is contained in:
Kenfe-Mickael Laventure 2017-07-19 12:13:25 +02:00
parent 79d04ded4e
commit 651aaff74e
No known key found for this signature in database
GPG Key ID: 40CF16616B361216
14 changed files with 568 additions and 227 deletions

View File

@ -20,7 +20,7 @@ func BenchmarkContainerCreate(b *testing.B) {
b.Error(err) b.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("true")) spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
if err != nil { if err != nil {
b.Error(err) b.Error(err)
return return
@ -63,7 +63,7 @@ func BenchmarkContainerStart(b *testing.B) {
b.Error(err) b.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("true")) spec, err := GenerateSpec(WithImageConfig(ctx, image), withTrue())
if err != nil { if err != nil {
b.Error(err) b.Error(err)
return return

View File

@ -1,3 +1,5 @@
// +build !windows
package containerd package containerd
import ( import (

View File

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"runtime"
"syscall" "syscall"
"testing" "testing"
"time" "time"
@ -17,11 +18,6 @@ import (
"github.com/containerd/containerd/testutil" "github.com/containerd/containerd/testutil"
) )
const (
defaultRoot = "/var/lib/containerd-test"
testImage = "docker.io/library/alpine:latest"
)
var ( var (
address string address string
noDaemon bool noDaemon bool
@ -29,7 +25,7 @@ var (
) )
func init() { 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.BoolVar(&noDaemon, "no-daemon", false, "Do not start a dedicated daemon for the tests")
flag.Parse() flag.Parse()
} }
@ -57,11 +53,15 @@ func TestMain(m *testing.M) {
defer cancel() defer cancel()
if !noDaemon { if !noDaemon {
os.RemoveAll(defaultRoot)
// setup a new containerd daemon if !testing.Short // setup a new containerd daemon if !testing.Short
cmd = exec.Command("containerd", cmd = exec.Command("containerd",
"--root", defaultRoot, "--root", defaultRoot,
"--address", address, "--address", address,
"--log-level", "debug",
) )
cmd.Stdout = buf
cmd.Stderr = buf cmd.Stderr = buf
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
cmd.Wait() cmd.Wait()
@ -94,14 +94,22 @@ func TestMain(m *testing.M) {
}).Info("running tests against containerd") }).Info("running tests against containerd")
// pull a seed image // pull a seed image
if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil { if runtime.GOOS != "windows" { // TODO: remove once pull is supported on windows
cmd.Process.Signal(syscall.SIGTERM) if _, err = client.Pull(ctx, testImage, WithPullUnpack); err != nil {
cmd.Wait() cmd.Process.Signal(syscall.SIGTERM)
fmt.Fprintf(os.Stderr, "%s: %s", err, buf.String()) 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) os.Exit(1)
} }
if err := client.Close(); err != nil { if err := client.Close(); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, "failed to close client", err)
} }
// run the test // run the test
@ -110,13 +118,15 @@ func TestMain(m *testing.M) {
if !noDaemon { if !noDaemon {
// tear down the daemon and resources created // tear down the daemon and resources created
if err := cmd.Process.Signal(syscall.SIGTERM); err != nil { 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 { 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 { 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) os.Exit(1)
} }
// only print containerd logs if the test failed // only print containerd logs if the test failed
@ -171,6 +181,11 @@ func TestNewClient(t *testing.T) {
} }
func TestImagePull(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) client, err := newClient(t, address)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

13
client_unix_test.go Normal file
View 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
View 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
View 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
}

View File

@ -6,13 +6,16 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"runtime"
"strings"
"sync" "sync"
"syscall" "syscall"
"testing" "testing"
"github.com/containerd/cgroups" // Register the typeurl
_ "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
specs "github.com/opencontainers/runtime-spec/specs-go"
) )
func empty() IOCreation { func empty() IOCreation {
@ -48,7 +51,7 @@ func TestNewContainer(t *testing.T) {
} }
defer client.Close() defer client.Close()
spec, err := GenerateSpec() spec, err := generateSpec()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -84,22 +87,26 @@ func TestContainerStart(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sh", "-c", "exit 7")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -151,23 +158,27 @@ func TestContainerOutput(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
expected = "kingkoye" expected = "kingkoye"
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("echo", expected)) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -207,7 +218,7 @@ func TestContainerOutput(t *testing.T) {
actual := stdout.String() actual := stdout.String()
// echo adds a new line // echo adds a new line
expected = expected + "\n" expected = expected + newLine
if actual != expected { if actual != expected {
t.Errorf("expected output %q but received %q", expected, actual) t.Errorf("expected output %q but received %q", expected, actual)
} }
@ -221,22 +232,26 @@ func TestContainerExec(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -260,10 +275,7 @@ func TestContainerExec(t *testing.T) {
// start an exec process without running the original container process info // start an exec process without running the original container process info
processSpec := spec.Process processSpec := spec.Process
processSpec.Args = []string{ withExecExitStatus(processSpec, 6)
"sh", "-c",
"exit 6",
}
execID := t.Name() + "_exec" execID := t.Name() + "_exec"
process, err := task.Exec(ctx, execID, processSpec, empty()) process, err := task.Exec(ctx, execID, processSpec, empty())
if err != nil { if err != nil {
@ -305,7 +317,7 @@ func TestContainerExec(t *testing.T) {
<-finished <-finished
} }
func TestContainerProcesses(t *testing.T) { func TestContainerPids(t *testing.T) {
client, err := newClient(t, address) client, err := newClient(t, address)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -313,22 +325,26 @@ func TestContainerProcesses(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -383,29 +399,33 @@ func TestContainerCloseIO(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("cat")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer container.Delete(ctx, WithRootFSDeletion) defer container.Delete(ctx, WithRootFSDeletion)
const expected = "hello\n" const expected = "hello" + newLine
stdout := bytes.NewBuffer(nil) stdout := bytes.NewBuffer(nil)
r, w, err := os.Pipe() r, w, err := os.Pipe()
@ -451,12 +471,25 @@ func TestContainerCloseIO(t *testing.T) {
output := stdout.String() 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 { if output != expected {
t.Errorf("expected output %q but received %q", expected, output) t.Errorf("expected output %q but received %q", expected, output)
} }
} }
func TestContainerAttach(t *testing.T) { 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) client, err := newClient(t, address)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -464,29 +497,33 @@ func TestContainerAttach(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("cat")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer container.Delete(ctx, WithRootFSDeletion) defer container.Delete(ctx, WithRootFSDeletion)
expected := "hello\n" expected := "hello" + newLine
stdout := bytes.NewBuffer(nil) stdout := bytes.NewBuffer(nil)
r, w, err := os.Pipe() r, w, err := os.Pipe()
@ -586,22 +623,26 @@ func TestDeleteRunningContainer(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithImage(image), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
@ -651,22 +692,26 @@ func TestContainerKill(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sh", "-c", "cat")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithImage(image), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return 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) { func TestContainerNoBinaryExists(t *testing.T) {
client, err := newClient(t, address) client, err := newClient(t, address)
if err != nil { if err != nil {
@ -806,30 +762,47 @@ func TestContainerNoBinaryExists(t *testing.T) {
defer client.Close() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("nothing")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
defer container.Delete(ctx, WithRootFSDeletion) defer container.Delete(ctx, WithRootFSDeletion)
if _, err := container.NewTask(ctx, Stdio); err == nil { task, err := container.NewTask(ctx, Stdio)
t.Error("NewTask should return an error when binary does not exist") 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() defer client.Close()
var ( var (
image Image
ctx, cancel = testContext() ctx, cancel = testContext()
id = t.Name() id = t.Name()
) )
defer cancel() 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 { if err != nil {
t.Error(err) t.Error(err)
return return
} }
spec, err := GenerateSpec(WithImageConfig(ctx, image), WithProcessArgs("sleep", "100")) container, err := client.NewContainer(ctx, id, WithSpec(spec), withNewRootFS(id, image))
if err != nil {
t.Error(err)
return
}
container, err := client.NewContainer(ctx, id, WithSpec(spec), WithNewRootFS(id, image))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return

View File

@ -258,9 +258,11 @@ func checkBlobPath(t *testing.T, cs Store, dgst digest.Digest) string {
t.Fatalf("error stating blob path: %v", err) t.Fatalf("error stating blob path: %v", err)
} }
// ensure that only read bits are set. if runtime.GOOS != "windows" {
if ((fi.Mode() & os.ModePerm) & 0333) != 0 { // ensure that only read bits are set.
t.Fatalf("incorrect permissions: %v", fi.Mode()) if ((fi.Mode() & os.ModePerm) & 0333) != 0 {
t.Fatalf("incorrect permissions: %v", fi.Mode())
}
} }
return path return path

View File

@ -3,6 +3,7 @@ package content
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"time" "time"
"github.com/containerd/containerd/errdefs" "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. // only allowing reads honoring the umask on creation.
// //
// This removes write and exec, only allowing read per the creation umask. // 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() { if size > 0 && size != fi.Size() {

51
helpers_unix_test.go Normal file
View 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
View 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
}
}

View File

@ -2,15 +2,11 @@ package testutil
import ( import (
"flag" "flag"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"testing" "testing"
"github.com/containerd/containerd/mount"
"github.com/stretchr/testify/assert"
) )
var rootEnabled bool var rootEnabled bool
@ -19,36 +15,6 @@ func init() {
flag.BoolVar(&rootEnabled, "test.root", false, "enable tests that require root") 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 // DumpDir will log out all of the contents of the provided directory to
// testing logger. // testing logger.
// //

42
testutil/helpers_unix.go Normal file
View 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)
}
}

View 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) {
}