Skip to content

Commit e285bae

Browse files
author
Zhou Hao
authored
Merge pull request #447 from liangchenye/unittest
add 'Runtime' struct to make test easier; add create test
2 parents 68ec7ee + 15577bd commit e285bae

File tree

3 files changed

+215
-47
lines changed

3 files changed

+215
-47
lines changed

error/runtime_spec.go

+16
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ const (
4343

4444
// DefaultFilesystems represents the error code of default filesystems test
4545
DefaultFilesystems
46+
47+
// CreateWithID represents the error code of 'create' lifecyle test with 'id' provided
48+
CreateWithID
49+
// CreateWithUniqueID represents the error code of 'create' lifecyle test with unique 'id' provided
50+
CreateWithUniqueID
51+
// CreateNewContainer represents the error code 'create' lifecyle test that creates new container
52+
CreateNewContainer
4653
)
4754

4855
type errorTemplate struct {
@@ -63,6 +70,9 @@ var (
6370
defaultFSRef = func(version string) (reference string, err error) {
6471
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
6572
}
73+
runtimeCreateRef = func(version string) (reference string, err error) {
74+
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
75+
}
6676
)
6777

6878
var ociErrors = map[SpecErrorCode]errorTemplate{
@@ -87,6 +97,12 @@ var ociErrors = map[SpecErrorCode]errorTemplate{
8797
// Config-Linux.md
8898
// Default Filesystems
8999
DefaultFilesystems: errorTemplate{Level: Should, Reference: defaultFSRef},
100+
101+
// Runtime.md
102+
// Create
103+
CreateWithID: errorTemplate{Level: Must, Reference: runtimeCreateRef},
104+
CreateWithUniqueID: errorTemplate{Level: Must, Reference: runtimeCreateRef},
105+
CreateNewContainer: errorTemplate{Level: Must, Reference: runtimeCreateRef},
90106
}
91107

92108
// NewError creates an Error referencing a spec violation. The error

validation/container.go

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package validation
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
10+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
11+
"github.com/opencontainers/runtime-tools/generate"
12+
)
13+
14+
// Runtime represents the basic requirement of a container runtime
15+
type Runtime struct {
16+
RuntimeCommand string
17+
BundleDir string
18+
ID string
19+
}
20+
21+
// NewRuntime create a runtime by command and the bundle directory
22+
func NewRuntime(runtimeCommand string, bundleDir string) (Runtime, error) {
23+
var r Runtime
24+
var err error
25+
r.RuntimeCommand, err = exec.LookPath(runtimeCommand)
26+
if err != nil {
27+
return Runtime{}, err
28+
}
29+
30+
r.BundleDir = bundleDir
31+
return r, err
32+
}
33+
34+
// SetConfig creates a 'config.json' by the generator
35+
func (r *Runtime) SetConfig(g *generate.Generator) error {
36+
if r.BundleDir == "" {
37+
return errors.New("Please set the bundle directory first")
38+
}
39+
return g.SaveToFile(filepath.Join(r.BundleDir, "config.json"), generate.ExportOptions{})
40+
}
41+
42+
// SetID sets the container ID
43+
func (r *Runtime) SetID(id string) {
44+
r.ID = id
45+
}
46+
47+
// Create a container
48+
func (r *Runtime) Create() error {
49+
var args []string
50+
args = append(args, "create")
51+
if r.ID != "" {
52+
args = append(args, r.ID)
53+
}
54+
55+
// TODO: following the spec, we need define the bundle, but 'runc' does not..
56+
// if r.BundleDir != "" {
57+
// args = append(args, r.BundleDir)
58+
// }
59+
cmd := exec.Command(r.RuntimeCommand, args...)
60+
cmd.Dir = r.BundleDir
61+
cmd.Stdin = os.Stdin
62+
cmd.Stdout = os.Stdout
63+
cmd.Stderr = os.Stderr
64+
return cmd.Run()
65+
}
66+
67+
// Start a container
68+
func (r *Runtime) Start() error {
69+
var args []string
70+
args = append(args, "start")
71+
if r.ID != "" {
72+
args = append(args, r.ID)
73+
}
74+
75+
cmd := exec.Command(r.RuntimeCommand, args...)
76+
cmd.Stdin = os.Stdin
77+
cmd.Stdout = os.Stdout
78+
cmd.Stderr = os.Stderr
79+
return cmd.Run()
80+
}
81+
82+
// State a container information
83+
func (r *Runtime) State() (rspecs.State, error) {
84+
var args []string
85+
args = append(args, "state")
86+
if r.ID != "" {
87+
args = append(args, r.ID)
88+
}
89+
90+
out, err := exec.Command(r.RuntimeCommand, args...).Output()
91+
if err != nil {
92+
return rspecs.State{}, err
93+
}
94+
95+
var state rspecs.State
96+
err = json.Unmarshal(out, &state)
97+
return state, err
98+
}
99+
100+
// Delete a container
101+
func (r *Runtime) Delete() error {
102+
var args []string
103+
args = append(args, "delete")
104+
if r.ID != "" {
105+
args = append(args, r.ID)
106+
}
107+
108+
cmd := exec.Command(r.RuntimeCommand, args...)
109+
return cmd.Run()
110+
}
111+
112+
// Clean deletes the container and removes the bundle file according to the input parameter
113+
func (r *Runtime) Clean(removeBundle bool) error {
114+
err := r.Delete()
115+
if err != nil {
116+
return err
117+
}
118+
119+
if removeBundle {
120+
os.RemoveAll(r.BundleDir)
121+
}
122+
123+
return nil
124+
}

validation/validation_test.go

+75-47
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package validation
22

33
import (
4-
"fmt"
54
"io/ioutil"
65
"os"
76
"os/exec"
87
"path/filepath"
98
"testing"
109

1110
"github.com/mrunalp/fileutils"
12-
"github.com/opencontainers/runtime-tools/generate"
11+
rspecs "github.com/opencontainers/runtime-spec/specs-go"
1312
"github.com/satori/go.uuid"
13+
"github.com/stretchr/testify/assert"
14+
15+
rerr "github.com/opencontainers/runtime-tools/error"
16+
"github.com/opencontainers/runtime-tools/generate"
1417
)
1518

1619
var (
@@ -24,81 +27,106 @@ func init() {
2427
}
2528
}
2629

27-
func runtimeValidate(runtime string, g *generate.Generator) error {
28-
// Find the runtime binary in the PATH
29-
runtimePath, err := exec.LookPath(runtime)
30+
func prepareBundle() (string, error) {
31+
// Setup a temporary test directory
32+
bundleDir, err := ioutil.TempDir("", "ocitest")
3033
if err != nil {
31-
return err
34+
return "", err
3235
}
3336

34-
// Setup a temporary test directory
35-
tmpDir, err := ioutil.TempDir("", "ocitest")
37+
// Untar the root fs
38+
untarCmd := exec.Command("tar", "-xf", "../rootfs.tar.gz", "-C", bundleDir)
39+
_, err = untarCmd.CombinedOutput()
3640
if err != nil {
37-
return err
41+
os.RemoveAll(bundleDir)
42+
return "", err
3843
}
39-
defer os.RemoveAll(tmpDir)
4044

41-
// Create bundle directory for the test container
42-
bundleDir := tmpDir + "/busybox"
43-
if err := os.MkdirAll(bundleDir, 0755); err != nil {
45+
return bundleDir, nil
46+
}
47+
48+
func getDefaultGenerator() *generate.Generator {
49+
g := generate.New()
50+
g.SetRootPath(".")
51+
g.SetProcessArgs([]string{"/runtimetest"})
52+
return &g
53+
}
54+
55+
func runtimeInsideValidate(g *generate.Generator) error {
56+
bundleDir, err := prepareBundle()
57+
if err != nil {
4458
return err
4559
}
46-
47-
// Untar the root fs
48-
untarCmd := exec.Command("tar", "-xf", "../rootfs.tar.gz", "-C", bundleDir)
49-
output, err := untarCmd.CombinedOutput()
60+
r, err := NewRuntime(runtime, bundleDir)
5061
if err != nil {
51-
fmt.Println(string(output))
62+
os.RemoveAll(bundleDir)
5263
return err
5364
}
54-
55-
// Copy the runtimetest binary to the rootfs
56-
err = fileutils.CopyFile("../runtimetest", filepath.Join(bundleDir, "runtimetest"))
65+
defer r.Clean(true)
66+
err = r.SetConfig(g)
5767
if err != nil {
5868
return err
5969
}
60-
61-
// Generate test configuration
62-
err = g.SaveToFile(filepath.Join(bundleDir, "config.json"), generate.ExportOptions{})
70+
err = fileutils.CopyFile("../runtimetest", filepath.Join(r.BundleDir, "runtimetest"))
6371
if err != nil {
6472
return err
6573
}
6674

67-
// TODO: Use a library to split run into create/start
68-
// Launch the OCI runtime
69-
containerID := uuid.NewV4()
70-
runtimeCmd := exec.Command(runtimePath, "run", containerID.String())
71-
runtimeCmd.Dir = bundleDir
72-
runtimeCmd.Stdin = os.Stdin
73-
runtimeCmd.Stdout = os.Stdout
74-
runtimeCmd.Stderr = os.Stderr
75-
if err = runtimeCmd.Run(); err != nil {
75+
r.SetID(uuid.NewV4().String())
76+
err = r.Create()
77+
if err != nil {
7678
return err
7779
}
78-
79-
return nil
80-
}
81-
82-
func getDefaultGenerator() *generate.Generator {
83-
g := generate.New()
84-
g.SetRootPath(".")
85-
g.SetProcessArgs([]string{"/runtimetest"})
86-
return &g
80+
return r.Start()
8781
}
8882

8983
func TestValidateBasic(t *testing.T) {
9084
g := getDefaultGenerator()
9185

92-
if err := runtimeValidate(runtime, g); err != nil {
93-
t.Errorf("%s failed validation: %v", runtime, err)
94-
}
86+
assert.Nil(t, runtimeInsideValidate(g))
9587
}
9688

9789
func TestValidateSysctls(t *testing.T) {
9890
g := getDefaultGenerator()
9991
g.AddLinuxSysctl("net.ipv4.ip_forward", "1")
10092

101-
if err := runtimeValidate(runtime, g); err != nil {
102-
t.Errorf("%s failed validation: %v", runtime, err)
93+
assert.Nil(t, runtimeInsideValidate(g))
94+
}
95+
96+
func TestValidateCreate(t *testing.T) {
97+
g := generate.New()
98+
g.SetRootPath(".")
99+
g.SetProcessArgs([]string{"ls"})
100+
101+
bundleDir, err := prepareBundle()
102+
assert.Nil(t, err)
103+
104+
r, err := NewRuntime(runtime, bundleDir)
105+
assert.Nil(t, err)
106+
defer r.Clean(true)
107+
108+
err = r.SetConfig(&g)
109+
assert.Nil(t, err)
110+
111+
containerID := uuid.NewV4().String()
112+
cases := []struct {
113+
id string
114+
errExpected bool
115+
err error
116+
}{
117+
{"", false, rerr.NewError(rerr.CreateWithID, "'Create' MUST generate an error if the ID is not provided", rspecs.Version)},
118+
{containerID, true, rerr.NewError(rerr.CreateNewContainer, "'Create' MUST create a new container", rspecs.Version)},
119+
{containerID, false, rerr.NewError(rerr.CreateWithUniqueID, "'Create' MUST generate an error if the ID provided is not unique", rspecs.Version)},
120+
}
121+
122+
for _, c := range cases {
123+
r.SetID(c.id)
124+
err := r.Create()
125+
assert.Equal(t, c.errExpected, err == nil, c.err.Error())
126+
127+
if err == nil {
128+
state, _ := r.State()
129+
assert.Equal(t, c.id, state.ID, c.err.Error())
130+
}
103131
}
104132
}

0 commit comments

Comments
 (0)