Skip to content

Commit e11b77f

Browse files
committed
validation: Use prove(1) as a TAP harness
Capture stdout and stderr from create invocations, because we don't want to pollute the TAP output with things like runc's: Incorrect Usage. ... (which is for some reason printed to stdout) or: runc: "create" requires exactly 1 argument(s) which is printed to stderr. Instead, show the captured stderr as a diagnostic, and hide the stdout completely. Unless stderr is empty, in which case show stdout in case it contains something useful. Most of these tests are broken because we aren't collecting the container exit code or post-start stdout. But the tests haven't been doing that since the create/start split in 15577bd (add runtime struct; add create test, 2017-08-24, #447) anyway [1]. This commit just makes that more obvious. The patsubst and wildcard Makefile syntax is documented in [2]. The $(VALIDATION_TESTS) rule uses the static pattern rule syntax [3]. And use 'console' instead of 'sh' in the README, because these are shell sessions, not scripts. See [4,5]. I don't have a working runc locally, so the mock results based on a dummy runtime script. [1]: #447 (comment) [2]: https://www.gnu.org/software/make/manual/html_node/Wildcard-Function.html [3]: https://www.gnu.org/software/make/manual/html_node/Static-Usage.html#Static-Usage [4]: https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting [5]: https://github.com/github/linguist/blob/v5.3.3/lib/linguist/languages.yml#L4249-L4260 Signed-off-by: W. Trevor King <[email protected]>
1 parent 1c2dca0 commit e11b77f

24 files changed

+679
-376
lines changed

Makefile

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ BUILDTAGS=
55
RUNTIME ?= runc
66
COMMIT=$(shell git rev-parse HEAD 2> /dev/null || true)
77
VERSION := ${shell cat ./VERSION}
8+
VALIDATION_TESTS ?= $(patsubst %.go,%.t,$(wildcard validation/*.go))
89

9-
all: tool runtimetest
10+
all: tool runtimetest validation-executables
1011

1112
tool:
1213
go build -tags "$(BUILDTAGS)" -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION}" -o oci-runtime-tool ./cmd/oci-runtime-tool
@@ -35,10 +36,18 @@ uninstall:
3536
rm -f $(PREFIX)/share/bash-completion/completions/oci-runtime-tool
3637

3738
clean:
38-
rm -f oci-runtime-tool runtimetest *.1
39+
rm -f oci-runtime-tool runtimetest *.1 $(VALIDATION_TESTS)
3940

40-
localvalidation: runtimetest
41-
RUNTIME=$(RUNTIME) go test -tags "$(BUILDTAGS)" ${TESTFLAGS} -v github.com/opencontainers/runtime-tools/validation
41+
localvalidation:
42+
RUNTIME=$(RUNTIME) prove $(VALIDATION_TESTS)
43+
44+
.PHONY: validation-executables
45+
validation-executables: $(VALIDATION_TESTS)
46+
47+
.PRECIOUS: $(VALIDATION_TESTS)
48+
.PHONY: $(VALIDATION_TESTS)
49+
$(VALIDATION_TESTS): %.t: %.go
50+
go build -tags "$(BUILDTAGS)" ${TESTFLAGS} -o $@ $<
4251

4352
.PHONY: test .gofmt .govet .golint
4453

README.md

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ To build from source code, runtime-tools requires Go 1.7.x or above.
88
[`oci-runtime-tool generate`][generate.1] generates [configuration JSON][config.json] for an [OCI bundle][bundle].
99
[OCI-compatible runtimes][runtime-spec] like [runC][] expect to read the configuration from `config.json`.
1010

11-
```sh
11+
```console
1212
$ oci-runtime-tool generate --output config.json
1313
$ cat config.json
1414
{
@@ -22,63 +22,114 @@ $ cat config.json
2222
[`oci-runtime-tool validate`][validate.1] validates an OCI bundle.
2323
The error message will be printed if the OCI bundle failed the validation procedure.
2424

25-
```sh
25+
```console
2626
$ oci-runtime-tool generate
2727
$ oci-runtime-tool validate
2828
INFO[0000] Bundle validation succeeded.
2929
```
3030

3131
## Testing OCI runtimes
3232

33-
```sh
33+
The runtime validation suite uses [`prove`][prove], which is packaged for most distributions (for example, it is in [Debian's `perl` package][debian-perl] and [Gentoo's `dev-lang/perl` package][gentoo-perl]).
34+
If you cannot install `prove`, you can probably run the test suite with another [TAP consumer][tap-consumers], although you'll have to edit the [`Makefile`](Makefile) to replace `prove`.
35+
36+
```console
37+
$ make runtimetest validation-executables
3438
$ sudo make RUNTIME=runc localvalidation
35-
RUNTIME=runc go test -tags "" -v github.com/opencontainers/runtime-tools/validation
36-
=== RUN TestValidateBasic
37-
TAP version 13
38-
ok 1 - root filesystem
39-
ok 2 - hostname
40-
ok 3 - mounts
41-
ok 4 - capabilities
42-
ok 5 - default symlinks
43-
ok 6 - default devices
44-
ok 7 - linux devices
45-
ok 8 - linux process
46-
ok 9 - masked paths
47-
ok 10 - oom score adj
48-
ok 11 - read only paths
49-
ok 12 - rlimits
50-
ok 13 - sysctls
51-
ok 14 - uid mappings
52-
ok 15 - gid mappings
53-
1..15
54-
--- PASS: TestValidateBasic (0.08s)
55-
=== RUN TestValidateSysctls
39+
RUNTIME=runc prove validation/linux_rootfs_propagation_shared.t validation/create.t validation/default.t validation/linux_readonly_paths.t validation/linux_masked_paths.t validation/mounts.t validation/process.t validation/root_readonly_false.t validation/linux_sysctl.t validation/linux_devices.t validation/linux_gid_mappings.t validation/process_oom_score_adj.t validation/process_capabilities.t validation/process_rlimits.t validation/root_readonly_true.t validation/linux_rootfs_propagation_unbindable.t validation/hostname.t validation/linux_uid_mappings.t
40+
validation/linux_rootfs_propagation_shared.t ...... Failed 1/19 subtests
41+
validation/create.t ............................... ok
42+
validation/default.t .............................. ok
43+
validation/linux_readonly_paths.t ................. ok
44+
validation/linux_masked_paths.t ................... Failed 1/19 subtests
45+
validation/mounts.t ............................... ok
46+
validation/process.t .............................. ok
47+
validation/root_readonly_false.t .................. ok
48+
validation/linux_sysctl.t ......................... ok
49+
validation/linux_devices.t ........................ ok
50+
validation/linux_gid_mappings.t ................... Failed 1/19 subtests
51+
validation/process_oom_score_adj.t ................ ok
52+
validation/process_capabilities.t ................. ok
53+
validation/process_rlimits.t ...................... ok
54+
validation/root_readonly_true.t ................... ok
55+
validation/linux_rootfs_propagation_unbindable.t .. failed to create the container
56+
rootfsPropagation=unbindable is not supported
57+
exit status 1
58+
validation/linux_rootfs_propagation_unbindable.t .. Dubious, test returned 1 (wstat 256, 0x100)
59+
No subtests run
60+
validation/hostname.t ............................. ok
61+
validation/linux_uid_mappings.t ................... failed to create the container
62+
User namespace mappings specified, but USER namespace isn't enabled in the config
63+
exit status 1
64+
validation/linux_uid_mappings.t ................... Dubious, test returned 1 (wstat 256, 0x100)
65+
No subtests run
66+
67+
Test Summary Report
68+
-------------------
69+
validation/linux_rootfs_propagation_shared.t (Wstat: 0 Tests: 19 Failed: 1)
70+
Failed test: 16
71+
validation/linux_masked_paths.t (Wstat: 0 Tests: 19 Failed: 1)
72+
Failed test: 13
73+
validation/linux_gid_mappings.t (Wstat: 0 Tests: 19 Failed: 1)
74+
Failed test: 19
75+
validation/linux_rootfs_propagation_unbindable.t (Wstat: 256 Tests: 0 Failed: 0)
76+
Non-zero exit status: 1
77+
Parse errors: No plan found in TAP output
78+
validation/linux_uid_mappings.t (Wstat: 256 Tests: 0 Failed: 0)
79+
Non-zero exit status: 1
80+
Parse errors: No plan found in TAP output
81+
Files=18, Tests=271, 31 wallclock secs ( 0.06 usr 0.01 sys + 0.52 cusr 0.21 csys = 0.80 CPU)
82+
Result: FAIL
83+
make: *** [Makefile:42: localvalidation] Error 1
84+
```
85+
86+
If you are confident that the `validation/*.t` are current, you can run `prove` (or your preferred TAP consumer) directly to avoid unnecessary rebuilds:
87+
88+
```console
89+
$ sudo RUNTIME=runc prove -Q validation/*.t
90+
91+
Test Summary Report
92+
-------------------
93+
94+
Files=18, Tests=271, 31 wallclock secs ( 0.07 usr 0.01 sys + 0.51 cusr 0.22 csys = 0.81 CPU)
95+
Result: FAIL
96+
```
97+
98+
And you can run an individual test executable directly:
99+
100+
```console
101+
$ RUNTIME=runc validation/default.t
56102
TAP version 13
57103
ok 1 - root filesystem
58104
ok 2 - hostname
59-
ok 3 - mounts
60-
ok 4 - capabilities
61-
ok 5 - default symlinks
62-
ok 6 - default devices
63-
ok 7 - linux devices
64-
ok 8 - linux process
65-
ok 9 - masked paths
66-
ok 10 - oom score adj
67-
ok 11 - read only paths
68-
ok 12 - rlimits
69-
ok 13 - sysctls
70-
ok 14 - uid mappings
71-
ok 15 - gid mappings
72-
1..15
73-
--- PASS: TestValidateSysctls (0.20s)
74-
PASS
75-
ok github.com/opencontainers/runtime-tools/validation 0.281s
105+
ok 3 - process
106+
ok 4 - mounts
107+
ok 5 - user
108+
ok 6 - rlimits
109+
ok 7 - capabilities
110+
ok 8 - default symlinks
111+
ok 9 - default file system
112+
ok 10 - default devices
113+
ok 11 - linux devices
114+
ok 12 - linux process
115+
ok 13 - masked paths
116+
ok 14 - oom score adj
117+
ok 15 - read only paths
118+
ok 16 - rootfs propagation
119+
ok 17 - sysctls
120+
ok 18 - uid mappings
121+
ok 19 - gid mappings
122+
1..19
76123
```
77124

78125
[bundle]: https://github.com/opencontainers/runtime-spec/blob/master/bundle.md
79126
[config.json]: https://github.com/opencontainers/runtime-spec/blob/master/config.md
127+
[debian-perl]: https://packages.debian.org/stretch/perl
128+
[gentoo-perl]: https://packages.gentoo.org/packages/dev-lang/perl
129+
[prove]: http://search.cpan.org/~leont/Test-Harness-3.39/bin/prove
80130
[runC]: https://github.com/opencontainers/runc
81131
[runtime-spec]: https://github.com/opencontainers/runtime-spec
132+
[tap-consumers]: https://testanything.org/consumers.html
82133

83134
[generate.1]: man/oci-runtime-tool-generate.1.md
84135
[validate.1]: man/oci-runtime-tool-validate.1.md

validation/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/*.t

validation/create.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/mndrix/tap-go"
7+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
8+
"github.com/opencontainers/runtime-tools/generate"
9+
"github.com/opencontainers/runtime-tools/specerror"
10+
"github.com/opencontainers/runtime-tools/validation/util"
11+
"github.com/satori/go.uuid"
12+
)
13+
14+
func main() {
15+
t := tap.New()
16+
17+
g := generate.New()
18+
g.SetRootPath(".")
19+
g.SetProcessArgs([]string{"ls"})
20+
21+
bundleDir, err := util.PrepareBundle()
22+
if err != nil {
23+
util.Fatal(err)
24+
}
25+
26+
r, err := util.NewRuntime(util.RuntimeCommand, bundleDir)
27+
if err != nil {
28+
util.Fatal(err)
29+
}
30+
defer r.Clean(true)
31+
32+
err = r.SetConfig(&g)
33+
if err != nil {
34+
util.Fatal(err)
35+
}
36+
37+
containerID := uuid.NewV4().String()
38+
cases := []struct {
39+
id string
40+
errExpected bool
41+
err error
42+
}{
43+
{"", false, specerror.NewError(specerror.CreateWithBundlePathAndID, fmt.Errorf("create MUST generate an error if the ID is not provided"), rspecs.Version)},
44+
{containerID, true, specerror.NewError(specerror.CreateNewContainer, fmt.Errorf("create MUST create a new container"), rspecs.Version)},
45+
{containerID, false, specerror.NewError(specerror.CreateWithUniqueID, fmt.Errorf("create MUST generate an error if the ID provided is not unique"), rspecs.Version)},
46+
}
47+
48+
for _, c := range cases {
49+
r.SetID(c.id)
50+
stderr, err := r.Create()
51+
t.Ok((err == nil) == c.errExpected, c.err.(*specerror.Error).Err.Err.Error())
52+
t.Diagnostic(c.err.(*specerror.Error).Err.Reference)
53+
if err != nil {
54+
t.Diagnostic(err.Error())
55+
}
56+
if len(stderr) > 0 {
57+
t.Diagnostic(string(stderr))
58+
}
59+
60+
if err == nil {
61+
state, _ := r.State()
62+
t.Ok(state.ID == c.id, "")
63+
t.Diagnosticf("container PID: %d, state ID: %d", c.id, state.ID)
64+
}
65+
}
66+
67+
t.AutoPlan()
68+
}

validation/default.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package main
2+
3+
import (
4+
"github.com/opencontainers/runtime-tools/validation/util"
5+
)
6+
7+
func main() {
8+
g := util.GetDefaultGenerator()
9+
err := util.RuntimeInsideValidate(g, nil)
10+
if err != nil {
11+
util.Fatal(err)
12+
}
13+
}

validation/hostname.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
3+
import (
4+
"github.com/opencontainers/runtime-tools/validation/util"
5+
)
6+
7+
func main() {
8+
g := util.GetDefaultGenerator()
9+
g.SetHostname("hostname-specific")
10+
err := util.RuntimeInsideValidate(g, nil)
11+
if err != nil {
12+
util.Fatal(err)
13+
}
14+
}

validation/linux_devices.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package main
2+
3+
import (
4+
"os"
5+
6+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
7+
"github.com/opencontainers/runtime-tools/validation/util"
8+
)
9+
10+
func main() {
11+
g := util.GetDefaultGenerator()
12+
13+
// add char device
14+
cdev := rspecs.LinuxDevice{}
15+
cdev.Path = "/dev/test1"
16+
cdev.Type = "c"
17+
cdev.Major = 10
18+
cdev.Minor = 666
19+
cmode := os.FileMode(int32(432))
20+
cdev.FileMode = &cmode
21+
cuid := uint32(0)
22+
cdev.UID = &cuid
23+
cgid := uint32(0)
24+
cdev.GID = &cgid
25+
g.AddDevice(cdev)
26+
27+
// add block device
28+
bdev := rspecs.LinuxDevice{}
29+
bdev.Path = "/dev/test2"
30+
bdev.Type = "b"
31+
bdev.Major = 8
32+
bdev.Minor = 666
33+
bmode := os.FileMode(int32(432))
34+
bdev.FileMode = &bmode
35+
uid := uint32(0)
36+
bdev.UID = &uid
37+
gid := uint32(0)
38+
bdev.GID = &gid
39+
g.AddDevice(bdev)
40+
41+
// add fifo device
42+
pdev := rspecs.LinuxDevice{}
43+
pdev.Path = "/dev/test3"
44+
pdev.Type = "p"
45+
pdev.Major = 8
46+
pdev.Minor = 666
47+
pmode := os.FileMode(int32(432))
48+
pdev.FileMode = &pmode
49+
g.AddDevice(pdev)
50+
51+
err := util.RuntimeInsideValidate(g, nil)
52+
if err != nil {
53+
util.Fatal(err)
54+
}
55+
}

validation/linux_gid_mappings.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package main
2+
3+
import (
4+
"github.com/opencontainers/runtime-tools/validation/util"
5+
)
6+
7+
func main() {
8+
g := util.GetDefaultGenerator()
9+
g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(3200))
10+
err := util.RuntimeInsideValidate(g, nil)
11+
if err != nil {
12+
util.Fatal(err)
13+
}
14+
}

validation/linux_masked_paths.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package main
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/opencontainers/runtime-tools/validation/util"
8+
)
9+
10+
func main() {
11+
g := util.GetDefaultGenerator()
12+
g.AddLinuxMaskedPaths("/masktest")
13+
err := util.RuntimeInsideValidate(g, func(path string) error {
14+
pathName := filepath.Join(path, "masktest")
15+
return os.MkdirAll(pathName, 0700)
16+
})
17+
if err != nil {
18+
util.Fatal(err)
19+
}
20+
}

0 commit comments

Comments
 (0)