Skip to content

Commit 5a90e8a

Browse files
committed
add runtime struct; add create test
Signed-off-by: Liang Chenye <liangchenye@huawei.com>
1 parent 3db644c commit 5a90e8a

3 files changed

Lines changed: 215 additions & 47 deletions

File tree

validate/error.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ type ErrorCode int
1515
const (
1616
// DefaultFilesystems represents the error code of default filesystems test.
1717
DefaultFilesystems ErrorCode = iota
18+
19+
// CreateWithID represents the error code of 'create' lifecyle test with 'id' provided.
20+
CreateWithID
21+
// CreateWithUniqueID represents the error code of 'create' lifecyle test with unique 'id' provided.
22+
CreateWithUniqueID
23+
// CreateNewContainer represents the error code 'create' lifecyle test that creates new container.
24+
CreateNewContainer
1825
)
1926

2027
type errorTemplate struct {
@@ -29,6 +36,24 @@ var ociErrors = map[ErrorCode]errorTemplate{
2936
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
3037
},
3138
},
39+
CreateWithID: errorTemplate{
40+
Level: rfc2119.Must,
41+
Reference: func(version string) (reference string, err error) {
42+
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
43+
},
44+
},
45+
CreateWithUniqueID: errorTemplate{
46+
Level: rfc2119.Must,
47+
Reference: func(version string) (reference string, err error) {
48+
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
49+
},
50+
},
51+
CreateNewContainer: errorTemplate{
52+
Level: rfc2119.Must,
53+
Reference: func(version string) (reference string, err error) {
54+
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
55+
},
56+
},
3257
}
3358

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

validation/container.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
type Runtime struct {
15+
RuntimeCommand string
16+
BundleDir string
17+
ID string
18+
}
19+
20+
func NewRuntime(runtimeCommand string, bundleDir string) (Runtime, error) {
21+
var r Runtime
22+
var err error
23+
r.RuntimeCommand, err = exec.LookPath(runtimeCommand)
24+
if err != nil {
25+
return Runtime{}, err
26+
}
27+
28+
r.BundleDir = bundleDir
29+
return r, err
30+
}
31+
32+
func (r *Runtime) SetConfig(g *generate.Generator) error {
33+
if r.BundleDir == "" {
34+
return errors.New("Please set the bundle directory first")
35+
}
36+
return g.SaveToFile(filepath.Join(r.BundleDir, "config.json"), generate.ExportOptions{})
37+
}
38+
39+
func (r *Runtime) SetID(id string) {
40+
r.ID = id
41+
}
42+
43+
func (r *Runtime) Create() error {
44+
var args []string
45+
args = append(args, "create")
46+
if r.ID != "" {
47+
args = append(args, r.ID)
48+
}
49+
50+
// TODO: following the spec, we need define the bundle, but 'runc' does not..
51+
// if r.BundleDir != "" {
52+
// args = append(args, r.BundleDir)
53+
// }
54+
cmd := exec.Command(r.RuntimeCommand, args...)
55+
cmd.Dir = r.BundleDir
56+
cmd.Stdin = os.Stdin
57+
cmd.Stdout = os.Stdout
58+
cmd.Stderr = os.Stderr
59+
return cmd.Run()
60+
}
61+
62+
func (r *Runtime) Start() error {
63+
var args []string
64+
args = append(args, "start")
65+
if r.ID != "" {
66+
args = append(args, r.ID)
67+
}
68+
69+
cmd := exec.Command(r.RuntimeCommand, args...)
70+
cmd.Stdin = os.Stdin
71+
cmd.Stdout = os.Stdout
72+
cmd.Stderr = os.Stderr
73+
return cmd.Run()
74+
}
75+
76+
func (r *Runtime) State() (rspecs.State, error) {
77+
var args []string
78+
args = append(args, "state")
79+
if r.ID != "" {
80+
args = append(args, r.ID)
81+
}
82+
83+
out, err := exec.Command(r.RuntimeCommand, args...).Output()
84+
if err != nil {
85+
return rspecs.State{}, err
86+
}
87+
88+
var state rspecs.State
89+
err = json.Unmarshal(out, &state)
90+
return state, err
91+
}
92+
93+
func (r *Runtime) Delete() error {
94+
var args []string
95+
args = append(args, "delete")
96+
if r.ID != "" {
97+
args = append(args, r.ID)
98+
}
99+
100+
cmd := exec.Command(r.RuntimeCommand, args...)
101+
return cmd.Run()
102+
}
103+
104+
func (r *Runtime) Clean(removeBundle bool) error {
105+
err := r.Delete()
106+
if err != nil {
107+
return err
108+
}
109+
110+
if removeBundle {
111+
os.RemoveAll(r.BundleDir)
112+
}
113+
114+
return nil
115+
}

validation/validation_test.go

Lines changed: 75 additions & 47 deletions
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+
"github.com/opencontainers/runtime-tools/generate"
16+
"github.com/opencontainers/runtime-tools/validate"
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, validate.NewError(validate.CreateWithID, "'Create' MUST generate an error if the ID is not provided", rspecs.Version)},
118+
{containerID, true, validate.NewError(validate.CreateNewContainer, "'Create' MUST create a new container", rspecs.Version)},
119+
{containerID, false, validate.NewError(validate.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)