Skip to content

Commit

Permalink
add runtime struct; add create test
Browse files Browse the repository at this point in the history
Signed-off-by: Liang Chenye <liangchenye@huawei.com>
  • Loading branch information
liangchenye committed Aug 30, 2017
1 parent 68ec7ee commit 15577bd
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 47 deletions.
16 changes: 16 additions & 0 deletions error/runtime_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ const (

// DefaultFilesystems represents the error code of default filesystems test
DefaultFilesystems

// CreateWithID represents the error code of 'create' lifecyle test with 'id' provided
CreateWithID
// CreateWithUniqueID represents the error code of 'create' lifecyle test with unique 'id' provided
CreateWithUniqueID
// CreateNewContainer represents the error code 'create' lifecyle test that creates new container
CreateNewContainer
)

type errorTemplate struct {
Expand All @@ -63,6 +70,9 @@ var (
defaultFSRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "config-linux.md#default-filesystems"), nil
}
runtimeCreateRef = func(version string) (reference string, err error) {
return fmt.Sprintf(referenceTemplate, version, "runtime.md#create"), nil
}
)

var ociErrors = map[SpecErrorCode]errorTemplate{
Expand All @@ -87,6 +97,12 @@ var ociErrors = map[SpecErrorCode]errorTemplate{
// Config-Linux.md
// Default Filesystems
DefaultFilesystems: errorTemplate{Level: Should, Reference: defaultFSRef},

// Runtime.md
// Create
CreateWithID: errorTemplate{Level: Must, Reference: runtimeCreateRef},
CreateWithUniqueID: errorTemplate{Level: Must, Reference: runtimeCreateRef},
CreateNewContainer: errorTemplate{Level: Must, Reference: runtimeCreateRef},
}

// NewError creates an Error referencing a spec violation. The error
Expand Down
124 changes: 124 additions & 0 deletions validation/container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package validation

import (
"encoding/json"
"errors"
"os"
"os/exec"
"path/filepath"

rspecs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
)

// Runtime represents the basic requirement of a container runtime
type Runtime struct {
RuntimeCommand string
BundleDir string
ID string
}

// NewRuntime create a runtime by command and the bundle directory
func NewRuntime(runtimeCommand string, bundleDir string) (Runtime, error) {
var r Runtime
var err error
r.RuntimeCommand, err = exec.LookPath(runtimeCommand)
if err != nil {
return Runtime{}, err
}

r.BundleDir = bundleDir
return r, err
}

// SetConfig creates a 'config.json' by the generator
func (r *Runtime) SetConfig(g *generate.Generator) error {
if r.BundleDir == "" {
return errors.New("Please set the bundle directory first")
}
return g.SaveToFile(filepath.Join(r.BundleDir, "config.json"), generate.ExportOptions{})
}

// SetID sets the container ID
func (r *Runtime) SetID(id string) {
r.ID = id
}

// Create a container
func (r *Runtime) Create() error {
var args []string
args = append(args, "create")
if r.ID != "" {
args = append(args, r.ID)
}

// TODO: following the spec, we need define the bundle, but 'runc' does not..
// if r.BundleDir != "" {
// args = append(args, r.BundleDir)
// }
cmd := exec.Command(r.RuntimeCommand, args...)
cmd.Dir = r.BundleDir
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

// Start a container
func (r *Runtime) Start() error {
var args []string
args = append(args, "start")
if r.ID != "" {
args = append(args, r.ID)
}

cmd := exec.Command(r.RuntimeCommand, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

// State a container information
func (r *Runtime) State() (rspecs.State, error) {
var args []string
args = append(args, "state")
if r.ID != "" {
args = append(args, r.ID)
}

out, err := exec.Command(r.RuntimeCommand, args...).Output()
if err != nil {
return rspecs.State{}, err
}

var state rspecs.State
err = json.Unmarshal(out, &state)
return state, err
}

// Delete a container
func (r *Runtime) Delete() error {
var args []string
args = append(args, "delete")
if r.ID != "" {
args = append(args, r.ID)
}

cmd := exec.Command(r.RuntimeCommand, args...)
return cmd.Run()
}

// Clean deletes the container and removes the bundle file according to the input parameter
func (r *Runtime) Clean(removeBundle bool) error {
err := r.Delete()
if err != nil {
return err
}

if removeBundle {
os.RemoveAll(r.BundleDir)
}

return nil
}
122 changes: 75 additions & 47 deletions validation/validation_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package validation

import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/mrunalp/fileutils"
"github.com/opencontainers/runtime-tools/generate"
rspecs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/satori/go.uuid"
"github.com/stretchr/testify/assert"

rerr "github.com/opencontainers/runtime-tools/error"
"github.com/opencontainers/runtime-tools/generate"
)

var (
Expand All @@ -24,81 +27,106 @@ func init() {
}
}

func runtimeValidate(runtime string, g *generate.Generator) error {
// Find the runtime binary in the PATH
runtimePath, err := exec.LookPath(runtime)
func prepareBundle() (string, error) {
// Setup a temporary test directory
bundleDir, err := ioutil.TempDir("", "ocitest")
if err != nil {
return err
return "", err
}

// Setup a temporary test directory
tmpDir, err := ioutil.TempDir("", "ocitest")
// Untar the root fs
untarCmd := exec.Command("tar", "-xf", "../rootfs.tar.gz", "-C", bundleDir)
_, err = untarCmd.CombinedOutput()
if err != nil {
return err
os.RemoveAll(bundleDir)
return "", err
}
defer os.RemoveAll(tmpDir)

// Create bundle directory for the test container
bundleDir := tmpDir + "/busybox"
if err := os.MkdirAll(bundleDir, 0755); err != nil {
return bundleDir, nil
}

func getDefaultGenerator() *generate.Generator {
g := generate.New()
g.SetRootPath(".")
g.SetProcessArgs([]string{"/runtimetest"})
return &g
}

func runtimeInsideValidate(g *generate.Generator) error {
bundleDir, err := prepareBundle()
if err != nil {
return err
}

// Untar the root fs
untarCmd := exec.Command("tar", "-xf", "../rootfs.tar.gz", "-C", bundleDir)
output, err := untarCmd.CombinedOutput()
r, err := NewRuntime(runtime, bundleDir)
if err != nil {
fmt.Println(string(output))
os.RemoveAll(bundleDir)
return err
}

// Copy the runtimetest binary to the rootfs
err = fileutils.CopyFile("../runtimetest", filepath.Join(bundleDir, "runtimetest"))
defer r.Clean(true)
err = r.SetConfig(g)
if err != nil {
return err
}

// Generate test configuration
err = g.SaveToFile(filepath.Join(bundleDir, "config.json"), generate.ExportOptions{})
err = fileutils.CopyFile("../runtimetest", filepath.Join(r.BundleDir, "runtimetest"))
if err != nil {
return err
}

// TODO: Use a library to split run into create/start
// Launch the OCI runtime
containerID := uuid.NewV4()
runtimeCmd := exec.Command(runtimePath, "run", containerID.String())
runtimeCmd.Dir = bundleDir
runtimeCmd.Stdin = os.Stdin
runtimeCmd.Stdout = os.Stdout
runtimeCmd.Stderr = os.Stderr
if err = runtimeCmd.Run(); err != nil {
r.SetID(uuid.NewV4().String())
err = r.Create()
if err != nil {
return err
}

return nil
}

func getDefaultGenerator() *generate.Generator {
g := generate.New()
g.SetRootPath(".")
g.SetProcessArgs([]string{"/runtimetest"})
return &g
return r.Start()
}

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

if err := runtimeValidate(runtime, g); err != nil {
t.Errorf("%s failed validation: %v", runtime, err)
}
assert.Nil(t, runtimeInsideValidate(g))
}

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

if err := runtimeValidate(runtime, g); err != nil {
t.Errorf("%s failed validation: %v", runtime, err)
assert.Nil(t, runtimeInsideValidate(g))
}

func TestValidateCreate(t *testing.T) {
g := generate.New()
g.SetRootPath(".")
g.SetProcessArgs([]string{"ls"})

bundleDir, err := prepareBundle()
assert.Nil(t, err)

r, err := NewRuntime(runtime, bundleDir)
assert.Nil(t, err)
defer r.Clean(true)

err = r.SetConfig(&g)
assert.Nil(t, err)

containerID := uuid.NewV4().String()
cases := []struct {
id string
errExpected bool
err error
}{
{"", false, rerr.NewError(rerr.CreateWithID, "'Create' MUST generate an error if the ID is not provided", rspecs.Version)},
{containerID, true, rerr.NewError(rerr.CreateNewContainer, "'Create' MUST create a new container", rspecs.Version)},
{containerID, false, rerr.NewError(rerr.CreateWithUniqueID, "'Create' MUST generate an error if the ID provided is not unique", rspecs.Version)},
}

for _, c := range cases {
r.SetID(c.id)
err := r.Create()
assert.Equal(t, c.errExpected, err == nil, c.err.Error())

if err == nil {
state, _ := r.State()
assert.Equal(t, c.id, state.ID, c.err.Error())
}
}
}

0 comments on commit 15577bd

Please sign in to comment.