mirror of
https://github.com/opencontainers/runc.git
synced 2025-10-05 07:27:03 +08:00

This removes libcontainer's own error wrapping system, consisting of a few types and functions, aimed at typization, wrapping and unwrapping of errors, as well as saving error stack traces. Since Go 1.13 now provides its own error wrapping mechanism and a few related functions, it makes sense to switch to it. While doing that, improve some error messages so that they start with "error", "unable to", or "can't". A few things that are worth mentioning: 1. We lose stack traces (which were never shown anyway). 2. Users of libcontainer that relied on particular errors (like ContainerNotExists) need to switch to using errors.Is with the new errors defined in error.go. 3. encoding/json is unable to unmarshal the built-in error type, so we have to introduce initError and wrap the errors into it (basically passing the error as a string). This is the same as it was before, just a tad simpler (actually the initError is a type that got removed in commit afa844311; also suddenly ierr variable name makes sense now). Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
231 lines
5.7 KiB
Go
231 lines
5.7 KiB
Go
// +build linux
|
|
|
|
package libcontainer
|
|
|
|
import (
|
|
"errors"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/moby/sys/mountinfo"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/runc/libcontainer/utils"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func newTestRoot() (string, error) {
|
|
dir, err := ioutil.TempDir("", "libcontainer")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return dir, nil
|
|
}
|
|
|
|
func TestFactoryNew(t *testing.T) {
|
|
root, rerr := newTestRoot()
|
|
if rerr != nil {
|
|
t.Fatal(rerr)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
factory, err := New(root, Cgroupfs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if factory == nil {
|
|
t.Fatal("factory should not be nil")
|
|
}
|
|
lfactory, ok := factory.(*LinuxFactory)
|
|
if !ok {
|
|
t.Fatal("expected linux factory returned on linux based systems")
|
|
}
|
|
if lfactory.Root != root {
|
|
t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
|
|
}
|
|
|
|
if factory.Type() != "libcontainer" {
|
|
t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
|
|
}
|
|
}
|
|
|
|
func TestFactoryNewIntelRdt(t *testing.T) {
|
|
root, rerr := newTestRoot()
|
|
if rerr != nil {
|
|
t.Fatal(rerr)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
factory, err := New(root, Cgroupfs, IntelRdtFs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if factory == nil {
|
|
t.Fatal("factory should not be nil")
|
|
}
|
|
lfactory, ok := factory.(*LinuxFactory)
|
|
if !ok {
|
|
t.Fatal("expected linux factory returned on linux based systems")
|
|
}
|
|
if lfactory.Root != root {
|
|
t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
|
|
}
|
|
|
|
if factory.Type() != "libcontainer" {
|
|
t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
|
|
}
|
|
}
|
|
|
|
func TestFactoryNewTmpfs(t *testing.T) {
|
|
root, rerr := newTestRoot()
|
|
if rerr != nil {
|
|
t.Fatal(rerr)
|
|
}
|
|
defer os.RemoveAll(root)
|
|
factory, err := New(root, Cgroupfs, TmpfsRoot)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if factory == nil {
|
|
t.Fatal("factory should not be nil")
|
|
}
|
|
lfactory, ok := factory.(*LinuxFactory)
|
|
if !ok {
|
|
t.Fatal("expected linux factory returned on linux based systems")
|
|
}
|
|
if lfactory.Root != root {
|
|
t.Fatalf("expected factory root to be %q but received %q", root, lfactory.Root)
|
|
}
|
|
|
|
if factory.Type() != "libcontainer" {
|
|
t.Fatalf("unexpected factory type: %q, expected %q", factory.Type(), "libcontainer")
|
|
}
|
|
mounted, err := mountinfo.Mounted(lfactory.Root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !mounted {
|
|
t.Fatalf("Factory Root is not mounted")
|
|
}
|
|
mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(lfactory.Root))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(mounts) != 1 {
|
|
t.Fatalf("Factory Root is not listed in mounts list")
|
|
}
|
|
m := mounts[0]
|
|
if m.FSType != "tmpfs" {
|
|
t.Fatalf("FSType of root: %s, expected %s", m.FSType, "tmpfs")
|
|
}
|
|
if m.Source != "tmpfs" {
|
|
t.Fatalf("Source of root: %s, expected %s", m.Source, "tmpfs")
|
|
}
|
|
err = unix.Unmount(root, unix.MNT_DETACH)
|
|
if err != nil {
|
|
t.Error("failed to unmount root:", err)
|
|
}
|
|
}
|
|
|
|
func TestFactoryLoadNotExists(t *testing.T) {
|
|
root, rerr := newTestRoot()
|
|
if rerr != nil {
|
|
t.Fatal(rerr)
|
|
}
|
|
defer os.RemoveAll(root) //nolint: errcheck
|
|
factory, err := New(root, Cgroupfs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = factory.Load("nocontainer")
|
|
if err == nil {
|
|
t.Fatal("expected nil error loading non-existing container")
|
|
}
|
|
if !errors.Is(err, ErrNotExist) {
|
|
t.Fatalf("expected ErrNotExist, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFactoryLoadContainer(t *testing.T) {
|
|
root, err := newTestRoot()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(root) //nolint: errcheck
|
|
// setup default container config and state for mocking
|
|
var (
|
|
id = "1"
|
|
expectedHooks = configs.Hooks{
|
|
configs.Prestart: configs.HookList{
|
|
configs.CommandHook{Command: configs.Command{Path: "prestart-hook"}},
|
|
},
|
|
configs.Poststart: configs.HookList{
|
|
configs.CommandHook{Command: configs.Command{Path: "poststart-hook"}},
|
|
},
|
|
configs.Poststop: configs.HookList{
|
|
unserializableHook{},
|
|
configs.CommandHook{Command: configs.Command{Path: "poststop-hook"}},
|
|
},
|
|
}
|
|
expectedConfig = &configs.Config{
|
|
Rootfs: "/mycontainer/root",
|
|
Hooks: expectedHooks,
|
|
}
|
|
expectedState = &State{
|
|
BaseState: BaseState{
|
|
InitProcessPid: 1024,
|
|
Config: *expectedConfig,
|
|
},
|
|
}
|
|
)
|
|
if err := os.Mkdir(filepath.Join(root, id), 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := marshal(filepath.Join(root, id, stateFilename), expectedState); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
factory, err := New(root, Cgroupfs, IntelRdtFs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
container, err := factory.Load(id)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if container.ID() != id {
|
|
t.Fatalf("expected container id %q but received %q", id, container.ID())
|
|
}
|
|
config := container.Config()
|
|
if config.Rootfs != expectedConfig.Rootfs {
|
|
t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs)
|
|
}
|
|
expectedHooks[configs.Poststop] = expectedHooks[configs.Poststop][1:] // expect unserializable hook to be skipped
|
|
if !reflect.DeepEqual(config.Hooks, expectedHooks) {
|
|
t.Fatalf("expects hooks %q but received %q", expectedHooks, config.Hooks)
|
|
}
|
|
lcontainer, ok := container.(*linuxContainer)
|
|
if !ok {
|
|
t.Fatal("expected linux container on linux based systems")
|
|
}
|
|
if lcontainer.initProcess.pid() != expectedState.InitProcessPid {
|
|
t.Fatalf("expected init pid %d but received %d", expectedState.InitProcessPid, lcontainer.initProcess.pid())
|
|
}
|
|
}
|
|
|
|
func marshal(path string, v interface{}) error {
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close() //nolint: errcheck
|
|
return utils.WriteJSON(f, v)
|
|
}
|
|
|
|
type unserializableHook struct{}
|
|
|
|
func (unserializableHook) Run(*specs.State) error {
|
|
return nil
|
|
}
|