buildah/unshare/unshare_test.go

256 lines
6.6 KiB
Go

// +build linux
package unshare
import (
"bytes"
"encoding/json"
"io/ioutil"
"os"
"strconv"
"strings"
"syscall"
"testing"
"github.com/containers/storage/pkg/reexec"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/projectatomic/buildah/util"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func init() {
reexec.Register("report", report)
}
var (
CloneFlags = map[string]int{
"ipc": syscall.CLONE_NEWIPC,
"net": syscall.CLONE_NEWNET,
"mnt": syscall.CLONE_NEWNS,
"user": syscall.CLONE_NEWUSER,
"uts": syscall.CLONE_NEWUTS,
}
)
type Report struct {
Namespaces map[string]string
UIDMappings []specs.LinuxIDMapping
GIDMappings []specs.LinuxIDMapping
Pgrp int
Sid int
OOMScoreAdj int
}
func report() {
var report Report
report.Namespaces = make(map[string]string)
for name := range CloneFlags {
linkTarget, err := os.Readlink("/proc/self/ns/" + name)
if err != nil {
logrus.Errorf("error reading link /proc/self/ns/%s: %v", name, err)
os.Exit(1)
}
report.Namespaces[name] = linkTarget
}
report.Pgrp = syscall.Getpgrp()
sid, err := unix.Getsid(unix.Getpid())
if err != nil {
logrus.Errorf("error reading current session ID: %v", err)
os.Exit(1)
}
report.Sid = sid
oomBytes, err := ioutil.ReadFile("/proc/self/oom_score_adj")
if err != nil {
logrus.Errorf("error reading current oom_score_adj: %v", err)
os.Exit(1)
}
oomFields := strings.Fields(string(oomBytes))
if len(oomFields) != 1 {
logrus.Errorf("error parsing current oom_score_adj %q: wrong number of fields", string(oomBytes))
os.Exit(1)
}
oom, err := strconv.Atoi(oomFields[0])
if err != nil {
logrus.Errorf("error parsing current oom_score_adj %q: %v", oomFields[0], err)
os.Exit(1)
}
report.OOMScoreAdj = oom
uidmap, gidmap, err := util.GetHostIDMappings("")
if err != nil {
logrus.Errorf("error reading current ID mappings: %v", err)
os.Exit(1)
}
for _, m := range uidmap {
report.UIDMappings = append(report.UIDMappings, m)
}
for _, m := range gidmap {
report.GIDMappings = append(report.GIDMappings, m)
}
json.NewEncoder(os.Stdout).Encode(report)
}
func TestUnshareNamespaces(t *testing.T) {
for name, flag := range CloneFlags {
var report Report
buf := new(bytes.Buffer)
cmd := Command("report")
cmd.UnshareFlags = syscall.CLONE_NEWUSER | flag
cmd.UidMappings = []specs.LinuxIDMapping{{HostID: uint32(syscall.Getuid()), ContainerID: 0, Size: 1}}
cmd.GidMappings = []specs.LinuxIDMapping{{HostID: uint32(syscall.Getgid()), ContainerID: 0, Size: 1}}
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
t.Fatalf("run %q: %v: %s", name, err, buf.String())
break
}
if err = json.Unmarshal(buf.Bytes(), &report); err != nil {
t.Fatalf("error parsing results: %v", err)
break
}
for ns := range CloneFlags {
linkTarget, err := os.Readlink("/proc/self/ns/" + ns)
if err != nil {
t.Fatalf("error reading link /proc/self/ns/%s: %v", ns, err)
os.Exit(1)
}
if ns == name || ns == "user" { // we always create a new user namespace
if report.Namespaces[ns] == linkTarget {
t.Fatalf("child is still in our %q namespace", name)
os.Exit(1)
}
} else {
if report.Namespaces[ns] != linkTarget {
t.Fatalf("child is not in our %q namespace", name)
os.Exit(1)
}
}
}
}
}
func TestUnsharePgrp(t *testing.T) {
for _, same := range []bool{false, true} {
var report Report
buf := new(bytes.Buffer)
cmd := Command("report")
cmd.Setpgrp = !same
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
t.Fatalf("run: %v: %s", err, buf.String())
break
}
if err = json.Unmarshal(buf.Bytes(), &report); err != nil {
t.Fatalf("error parsing results: %v", err)
break
}
if (report.Pgrp == syscall.Getpgrp()) != same {
t.Fatalf("expected %d == %d to be %v", report.Pgrp, syscall.Getpgrp(), same)
}
}
}
func TestUnshareSid(t *testing.T) {
sid, err := unix.Getsid(unix.Getpid())
if err != nil {
t.Fatalf("error reading current session ID: %v", err)
}
for _, same := range []bool{false, true} {
var report Report
buf := new(bytes.Buffer)
cmd := Command("report")
cmd.Setsid = !same
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
t.Fatalf("run: %v: %s", err, buf.String())
break
}
if err = json.Unmarshal(buf.Bytes(), &report); err != nil {
t.Fatalf("error parsing results: %v", err)
break
}
if (report.Sid == sid) != same {
t.Fatalf("expected %d == %d to be %v", report.Sid, sid, same)
}
}
}
func TestUnshareOOMScoreAdj(t *testing.T) {
for _, adj := range []int{0, 1, 2, 3} {
var report Report
buf := new(bytes.Buffer)
cmd := Command("report")
cmd.OOMScoreAdj = adj
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
t.Fatalf("run: %v: %s", err, buf.String())
break
}
if err = json.Unmarshal(buf.Bytes(), &report); err != nil {
t.Fatalf("error parsing results: %v", err)
break
}
if report.OOMScoreAdj != adj {
t.Fatalf("saw oom_score_adj %d to be %v", adj, report.OOMScoreAdj)
}
}
}
func TestUnshareIDMappings(t *testing.T) {
var report Report
buf := new(bytes.Buffer)
cmd := Command("report")
cmd.UnshareFlags = syscall.CLONE_NEWUSER
cmd.UidMappings = []specs.LinuxIDMapping{{HostID: uint32(syscall.Getuid()), ContainerID: 0, Size: 1}}
cmd.GidMappings = []specs.LinuxIDMapping{{HostID: uint32(syscall.Getgid()), ContainerID: 0, Size: 1}}
cmd.Stdout = buf
cmd.Stderr = buf
err := cmd.Run()
if err != nil {
t.Fatalf("run: %v: %s", err, buf.String())
}
if err = json.Unmarshal(buf.Bytes(), &report); err != nil {
t.Fatalf("error parsing results: %v", err)
}
if len(cmd.UidMappings) != len(report.UIDMappings) {
t.Fatalf("set %d UID mappings, read %d instead", len(cmd.UidMappings), len(report.UIDMappings))
}
for i := range cmd.UidMappings {
if cmd.UidMappings[i].ContainerID != report.UIDMappings[i].ContainerID ||
cmd.UidMappings[i].HostID != report.UIDMappings[i].HostID ||
cmd.UidMappings[i].Size != report.UIDMappings[i].Size {
t.Fatalf("uid map entry %#v != %#v", cmd.UidMappings[i], report.UIDMappings[i])
}
}
if len(cmd.GidMappings) != len(report.GIDMappings) {
t.Fatalf("set %d GID mappings, read %d instead", len(cmd.GidMappings), len(report.GIDMappings))
}
for i := range cmd.GidMappings {
if cmd.GidMappings[i].ContainerID != report.GIDMappings[i].ContainerID ||
cmd.GidMappings[i].HostID != report.GIDMappings[i].HostID ||
cmd.GidMappings[i].Size != report.GIDMappings[i].Size {
t.Fatalf("gid map entry %#v != %#v", cmd.GidMappings[i], report.GIDMappings[i])
}
}
}