mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
3bc606e9d3
1. Use t.TempDir instead of ioutil.TempDir. This means no need for an explicit cleanup, which removes some code, including newTestBundle and newTestRoot. 2. Move newRootfs invocation down to newTemplateConfig, removing a need for explicit rootfs creation. Also, remove rootfs from tParam as it is no longer needed (there was a since test case in which two containers shared the same rootfs, but it does not look like it's required for the test). Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
185 lines
4.4 KiB
Go
185 lines
4.4 KiB
Go
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
)
|
|
|
|
var busyboxTar string
|
|
|
|
// init makes sure the container images are downloaded,
|
|
// and initializes busyboxTar. If images can't be downloaded,
|
|
// we are unable to run any tests, so panic.
|
|
func init() {
|
|
// Figure out path to get-images.sh. Note it won't work
|
|
// in case the compiled test binary is moved elsewhere.
|
|
_, ex, _, _ := runtime.Caller(0)
|
|
getImages, err := filepath.Abs(filepath.Join(filepath.Dir(ex), "..", "..", "tests", "integration", "get-images.sh"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
// Call it to make sure images are downloaded, and to get the paths.
|
|
out, err := exec.Command(getImages).CombinedOutput()
|
|
if err != nil {
|
|
panic(fmt.Errorf("getImages error %w (output: %s)", err, out))
|
|
}
|
|
// Extract the value of BUSYBOX_IMAGE.
|
|
found := regexp.MustCompile(`(?m)^BUSYBOX_IMAGE=(.*)$`).FindSubmatchIndex(out)
|
|
if len(found) < 4 {
|
|
panic(fmt.Errorf("unable to find BUSYBOX_IMAGE=<value> in %q", out))
|
|
}
|
|
busyboxTar = string(out[found[2]:found[3]])
|
|
// Finally, check the file is present
|
|
if _, err := os.Stat(busyboxTar); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func ptrInt(v int) *int {
|
|
return &v
|
|
}
|
|
|
|
func newStdBuffers() *stdBuffers {
|
|
return &stdBuffers{
|
|
Stdin: bytes.NewBuffer(nil),
|
|
Stdout: bytes.NewBuffer(nil),
|
|
Stderr: bytes.NewBuffer(nil),
|
|
}
|
|
}
|
|
|
|
type stdBuffers struct {
|
|
Stdin *bytes.Buffer
|
|
Stdout *bytes.Buffer
|
|
Stderr *bytes.Buffer
|
|
}
|
|
|
|
func (b *stdBuffers) String() string {
|
|
s := []string{}
|
|
if b.Stderr != nil {
|
|
s = append(s, b.Stderr.String())
|
|
}
|
|
if b.Stdout != nil {
|
|
s = append(s, b.Stdout.String())
|
|
}
|
|
return strings.Join(s, "|")
|
|
}
|
|
|
|
// ok fails the test if an err is not nil.
|
|
func ok(t testing.TB, err error) {
|
|
t.Helper()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func waitProcess(p *libcontainer.Process, t *testing.T) {
|
|
t.Helper()
|
|
status, err := p.Wait()
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if !status.Success() {
|
|
t.Fatalf("unexpected status: %v", status)
|
|
}
|
|
}
|
|
|
|
// newRootfs creates a new tmp directory and copies the busybox root
|
|
// filesystem to it.
|
|
func newRootfs(t *testing.T) string {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
if err := copyBusybox(dir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return dir
|
|
}
|
|
|
|
func remove(dir string) {
|
|
_ = os.RemoveAll(dir)
|
|
}
|
|
|
|
// copyBusybox copies the rootfs for a busybox container created for the test image
|
|
// into the new directory for the specific test
|
|
func copyBusybox(dest string) error {
|
|
out, err := exec.Command("sh", "-c", fmt.Sprintf("tar --exclude './dev/*' -C %q -xf %q", dest, busyboxTar)).CombinedOutput()
|
|
if err != nil {
|
|
return fmt.Errorf("untar error %w: %q", err, out)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func newContainer(t *testing.T, config *configs.Config) (libcontainer.Container, error) {
|
|
name := strings.ReplaceAll(t.Name(), "/", "_") + strconv.FormatInt(-int64(time.Now().Nanosecond()), 35)
|
|
root := t.TempDir()
|
|
|
|
f, err := libcontainer.New(root, libcontainer.Cgroupfs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if config.Cgroups != nil && config.Cgroups.Parent == "system.slice" {
|
|
f, err = libcontainer.New(root, libcontainer.SystemdCgroups)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return f.Create(name, config)
|
|
}
|
|
|
|
// runContainer runs the container with the specific config and arguments
|
|
//
|
|
// buffers are returned containing the STDOUT and STDERR output for the run
|
|
// along with the exit code and any go error
|
|
func runContainer(t *testing.T, config *configs.Config, console string, args ...string) (buffers *stdBuffers, exitCode int, err error) {
|
|
container, err := newContainer(t, config)
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
defer destroyContainer(container)
|
|
buffers = newStdBuffers()
|
|
process := &libcontainer.Process{
|
|
Cwd: "/",
|
|
Args: args,
|
|
Env: standardEnvironment,
|
|
Stdin: buffers.Stdin,
|
|
Stdout: buffers.Stdout,
|
|
Stderr: buffers.Stderr,
|
|
Init: true,
|
|
}
|
|
|
|
err = container.Run(process)
|
|
if err != nil {
|
|
return buffers, -1, err
|
|
}
|
|
ps, err := process.Wait()
|
|
if err != nil {
|
|
return buffers, -1, err
|
|
}
|
|
status := ps.Sys().(syscall.WaitStatus)
|
|
if status.Exited() {
|
|
exitCode = status.ExitStatus()
|
|
} else if status.Signaled() {
|
|
exitCode = -int(status.Signal())
|
|
} else {
|
|
return buffers, -1, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func destroyContainer(container libcontainer.Container) {
|
|
_ = container.Destroy()
|
|
}
|