Files
podman/pkg/util/utils_test.go
Giuseppe Scrivano 419efb9eb5 util: specify a not empty pause dir for root too
commit b3014c1c69d5870104aa45f7caae7af041094171 changed
GetRootlessRuntimeDir() to return an empty string for root, so that
its value is not exported as XDG_RUNTIME_DIR, and other programs like
crun can use a better default.

Now GetRootlessPauseProcessPidPath() uses homedir.GetRuntimeDir().
The homedir.GetRuntimeDir() function returns a value also when running
as root so it can be used inside a nested Podman.

Closes: https://github.com/containers/podman/issues/22327

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-05-06 12:14:00 +02:00

773 lines
16 KiB
Go

package util
import (
"fmt"
"math"
"sort"
"testing"
"time"
"github.com/containers/storage/pkg/idtools"
stypes "github.com/containers/storage/types"
ruser "github.com/moby/sys/user"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
func BreakInsert(mapping []idtools.IDMap, extension idtools.IDMap) (result []idtools.IDMap) {
result = breakInsert(mapping, extension)
result = sortAndMergeConsecutiveMappings(result)
return result
}
//#####################
func TestBreakInsert1(t *testing.T) {
// extension below mapping
mapping := []idtools.IDMap{
{
ContainerID: 1000,
HostID: 1000,
Size: 1,
},
}
extension := idtools.IDMap{
ContainerID: 2,
HostID: 2,
Size: 1,
}
expectedResult := []idtools.IDMap{
{
ContainerID: 2,
HostID: 2,
Size: 1,
},
{
ContainerID: 1000,
HostID: 1000,
Size: 1,
},
}
result := BreakInsert(mapping, extension)
assert.Equal(t, result, expectedResult)
}
func TestBreakInsert2(t *testing.T) {
// extension below mapping
mapping := []idtools.IDMap{
{
ContainerID: 1000,
HostID: 1001,
Size: 2,
},
}
extension := idtools.IDMap{
ContainerID: 2,
HostID: 3,
Size: 2,
}
expectedResult := []idtools.IDMap{
{
ContainerID: 2,
HostID: 3,
Size: 2,
},
{
ContainerID: 1000,
HostID: 1001,
Size: 2,
},
}
result := BreakInsert(mapping, extension)
assert.Equal(t, expectedResult, result)
}
func TestBreakInsert3(t *testing.T) {
// extension above mapping
mapping := []idtools.IDMap{
{
ContainerID: 2,
HostID: 3,
Size: 2,
},
}
extension := idtools.IDMap{
ContainerID: 1000,
HostID: 1001,
Size: 2,
}
expectedResult := []idtools.IDMap{
{
ContainerID: 2,
HostID: 3,
Size: 2,
},
{
ContainerID: 1000,
HostID: 1001,
Size: 2,
},
}
result := BreakInsert(mapping, extension)
assert.Equal(t, expectedResult, result)
}
func TestBreakInsert4(t *testing.T) {
// extension right below mapping
mapping := []idtools.IDMap{
{
ContainerID: 4,
HostID: 5,
Size: 4,
},
}
extension := idtools.IDMap{
ContainerID: 1,
HostID: 1,
Size: 3,
}
expectedResult := []idtools.IDMap{
{
ContainerID: 1,
HostID: 1,
Size: 3,
},
{
ContainerID: 4,
HostID: 5,
Size: 4,
},
}
result := BreakInsert(mapping, extension)
assert.Equal(t, expectedResult, result)
}
func TestSortAndMergeConsecutiveMappings(t *testing.T) {
// Extension and mapping are consecutive
mapping := []idtools.IDMap{
{
ContainerID: 1,
HostID: 1,
Size: 3,
},
{
ContainerID: 4,
HostID: 4,
Size: 4,
},
}
expectedResult := []idtools.IDMap{
{
ContainerID: 1,
HostID: 1,
Size: 7,
},
}
result := sortAndMergeConsecutiveMappings(mapping)
assert.Equal(t, expectedResult, result)
}
//#####################
func TestParseIDMap(t *testing.T) {
mapSpec := []string{"+100000:@1002:1"}
parentMapping := []ruser.IDMap{
{
ID: int64(20),
ParentID: int64(1002),
Count: 1,
},
}
expectedResult := []idtools.IDMap{
{
ContainerID: 100000,
HostID: 20,
Size: 1,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMapSizeMissing(t *testing.T) {
// Size is 1 if not provided
mapSpec := []string{"+100000:@1002"}
parentMapping := []ruser.IDMap{
{
ID: int64(20),
ParentID: int64(1002),
Count: 1,
},
}
expectedResult := []idtools.IDMap{
{
ContainerID: 100000,
HostID: 20,
Size: 1,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMap2(t *testing.T) {
mapSpec := []string{"0:2000:100000", "+1:100:1"}
parentMapping := []ruser.IDMap(nil)
expectedResult := []idtools.IDMap{
{
ContainerID: 0,
HostID: 2000,
Size: 1,
},
{
ContainerID: 1,
HostID: 100,
Size: 1,
},
{
ContainerID: 2,
HostID: 2002,
Size: 99998,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMap3(t *testing.T) {
mapSpec := []string{"0:0:20", "24:24:6", "+7:1000:2", "+12:2000:3", "+18:3000:7"}
parentMapping := []ruser.IDMap(nil)
expectedResult := []idtools.IDMap{
{
ContainerID: 0,
HostID: 0,
Size: 7,
},
{
ContainerID: 7,
HostID: 1000,
Size: 2,
},
{
ContainerID: 9,
HostID: 9,
Size: 3,
},
{
ContainerID: 12,
HostID: 2000,
Size: 3,
},
{
ContainerID: 15,
HostID: 15,
Size: 3,
},
{
ContainerID: 18,
HostID: 3000,
Size: 7,
},
{
ContainerID: 25,
HostID: 25,
Size: 5,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMap4(t *testing.T) {
mapSpec := []string{"0:0:20", "+10:1:1"}
parentMapping := []ruser.IDMap(nil)
expectedResult := []idtools.IDMap{
{
ContainerID: 0,
HostID: 0,
Size: 1,
},
{
ContainerID: 2,
HostID: 2,
Size: 8,
},
{
ContainerID: 10,
HostID: 1,
Size: 1,
},
{
ContainerID: 11,
HostID: 11,
Size: 9,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMap5(t *testing.T) {
mapSpec := []string{"0:20:10", "15:35:10", "+8:23:16"}
parentMapping := []ruser.IDMap(nil)
expectedResult := []idtools.IDMap{
{
ContainerID: 0,
HostID: 20,
Size: 3,
},
{
ContainerID: 8,
HostID: 23,
Size: 16,
},
{
ContainerID: 24,
HostID: 44,
Size: 1,
},
}
result, err := ParseIDMap(
mapSpec,
"UID",
parentMapping,
)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResult, result)
}
func TestParseIDMapUserGroupFlags(t *testing.T) {
mapSpec := []string{"u1:3:1", "g2:4:2"}
parentMapping := []ruser.IDMap(nil)
expectedResultUser := []idtools.IDMap{
{
ContainerID: 1,
HostID: 3,
Size: 1,
},
}
expectedResultGroup := []idtools.IDMap{
{
ContainerID: 2,
HostID: 4,
Size: 2,
},
}
result, err := ParseIDMap(mapSpec, "UID", parentMapping)
assert.Equal(t, nil, err)
assert.Equal(t, expectedResultUser, result)
result, err = ParseIDMap(mapSpec, "GID", parentMapping)
assert.Equal(t, err, nil)
assert.Equal(t, expectedResultGroup, result)
}
func TestParseAutoIDMap(t *testing.T) {
result, err := parseAutoIDMap("3:4:5", "UID", []ruser.IDMap{})
assert.Equal(t, err, nil)
assert.Equal(t, result, []idtools.IDMap{
{
ContainerID: 3,
HostID: 4,
Size: 5,
},
})
}
func TestParseAutoIDMapRelative(t *testing.T) {
parentMapping := []ruser.IDMap{
{
ID: 0,
ParentID: 1000,
Count: 1,
},
{
ID: 1,
ParentID: 100000,
Count: 65536,
},
}
result, err := parseAutoIDMap("100:@100000:1", "UID", parentMapping)
assert.Equal(t, err, nil)
assert.Equal(t, result, []idtools.IDMap{
{
ContainerID: 100,
HostID: 1,
Size: 1,
},
})
}
func TestFillIDMap(t *testing.T) {
availableRanges := [][2]int{{0, 10}, {10000, 20000}}
idmap := []idtools.IDMap{
{
ContainerID: 1,
HostID: 1000,
Size: 10,
},
{
ContainerID: 30,
HostID: 2000,
Size: 20,
},
}
expectedResult := []idtools.IDMap{
{
ContainerID: 0,
HostID: 0,
Size: 1,
},
{
ContainerID: 1,
HostID: 1000,
Size: 10,
},
{
ContainerID: 11,
HostID: 1,
Size: 9,
},
{
ContainerID: 20,
HostID: 10000,
Size: 10,
},
{
ContainerID: 30,
HostID: 2000,
Size: 20,
},
{
ContainerID: 50,
HostID: 10010,
Size: 9990,
},
}
result := fillIDMap(idmap, availableRanges)
assert.Equal(t, expectedResult, result)
}
func TestGetAvailableIDRanges(t *testing.T) {
all := [][2]int{{0, 30}, {50, 70}}
used := [][2]int{{2, 4}, {25, 55}}
expectedResult := [][2]int{{0, 2}, {4, 25}, {55, 70}}
result := getAvailableIDRanges(all, used)
assert.Equal(t, expectedResult, result)
}
func TestValidateSysctls(t *testing.T) {
strSlice := []string{"net.core.test1=4", "kernel.msgmax=2"}
result, _ := ValidateSysctls(strSlice)
assert.Equal(t, result["net.core.test1"], "4")
}
func TestValidateSysctlBadSysctl(t *testing.T) {
strSlice := []string{"BLAU=BLUE", "GELB^YELLOW"}
_, err := ValidateSysctls(strSlice)
assert.Error(t, err)
}
func TestValidateSysctlBadSysctlWithExtraSpaces(t *testing.T) {
expectedError := "'%s' is invalid, extra spaces found"
// should fail fast on first sysctl
strSlice1 := []string{
"net.ipv4.ping_group_range = 0 0",
"net.ipv4.ping_group_range=0 0 ",
}
_, err := ValidateSysctls(strSlice1)
assert.Error(t, err)
assert.Equal(t, err.Error(), fmt.Sprintf(expectedError, strSlice1[0]))
// should fail on second sysctl
strSlice2 := []string{
"net.ipv4.ping_group_range=0 0",
"net.ipv4.ping_group_range=0 0 ",
}
_, err = ValidateSysctls(strSlice2)
assert.Error(t, err)
assert.Equal(t, err.Error(), fmt.Sprintf(expectedError, strSlice2[1]))
}
func TestCoresToPeriodAndQuota(t *testing.T) {
cores := 1.0
expectedPeriod := DefaultCPUPeriod
expectedQuota := int64(DefaultCPUPeriod)
actualPeriod, actualQuota := CoresToPeriodAndQuota(cores)
assert.Equal(t, actualPeriod, expectedPeriod, "Period does not match")
assert.Equal(t, actualQuota, expectedQuota, "Quota does not match")
}
func TestPeriodAndQuotaToCores(t *testing.T) {
var (
period uint64 = 100000
quota int64 = 50000
expectedCores = 0.5
)
assert.Equal(t, PeriodAndQuotaToCores(period, quota), expectedCores)
}
func TestParseInputTime(t *testing.T) {
tm, err := ParseInputTime("1.5", true)
if err != nil {
t.Errorf("expected error to be nil but was: %v", err)
}
expected, err := time.ParseInLocation(time.RFC3339Nano, "1970-01-01T00:00:01.500000000Z", time.UTC)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, expected, tm)
}
func TestConvertMappings(t *testing.T) {
start := []specs.LinuxIDMapping{
{
ContainerID: 1,
HostID: 2,
Size: 3,
},
{
ContainerID: 4,
HostID: 5,
Size: 6,
},
}
converted := RuntimeSpecToIDtools(start)
convertedBack := IDtoolsToRuntimeSpec(converted)
assert.Equal(t, len(start), len(convertedBack))
for i := range start {
assert.Equal(t, start[i].ContainerID, convertedBack[i].ContainerID)
assert.Equal(t, start[i].HostID, convertedBack[i].HostID)
assert.Equal(t, start[i].Size, convertedBack[i].Size)
}
}
func TestConvertTimeout(t *testing.T) {
timeout := ConvertTimeout(0)
assert.Equal(t, uint(0), timeout)
timeout = ConvertTimeout(100)
assert.Equal(t, uint(100), timeout)
timeout = ConvertTimeout(-1)
assert.Equal(t, uint(math.MaxUint32), timeout)
timeout = ConvertTimeout(-100)
assert.Equal(t, uint(math.MaxUint32), timeout)
}
func TestGetRootlessKeepIDMapping(t *testing.T) {
tests := []struct {
uid, gid int
uids, gids []idtools.IDMap
expectedOptions *stypes.IDMappingOptions
expectedUID, expectedGID int
expectedError error
}{
{
uid: 1000,
gid: 1000,
uids: []idtools.IDMap{},
gids: []idtools.IDMap{},
expectedOptions: &stypes.IDMappingOptions{
HostUIDMapping: false,
HostGIDMapping: false,
UIDMap: []idtools.IDMap{{ContainerID: 1000, HostID: 0, Size: 1}},
GIDMap: []idtools.IDMap{{ContainerID: 1000, HostID: 0, Size: 1}},
},
expectedUID: 1000,
expectedGID: 1000,
},
{
uid: 0,
gid: 0,
uids: []idtools.IDMap{{ContainerID: 0, HostID: 100000, Size: 65536}},
gids: []idtools.IDMap{{ContainerID: 0, HostID: 100000, Size: 65536}},
expectedOptions: &stypes.IDMappingOptions{
HostUIDMapping: false,
HostGIDMapping: false,
UIDMap: []idtools.IDMap{{ContainerID: 0, HostID: 0, Size: 1}, {ContainerID: 1, HostID: 1, Size: 65536}},
GIDMap: []idtools.IDMap{{ContainerID: 0, HostID: 0, Size: 1}, {ContainerID: 1, HostID: 1, Size: 65536}},
},
expectedUID: 0,
expectedGID: 0,
},
}
for _, test := range tests {
options, uid, gid, err := getRootlessKeepIDMapping(test.uid, test.gid, test.uids, test.gids)
assert.Nil(t, err)
assert.Equal(t, test.expectedOptions, options)
assert.Equal(t, test.expectedUID, uid)
assert.Equal(t, test.expectedGID, gid)
}
}
func getDefaultMountOptionsNoStat(path string) (defaultMountOptions, error) {
return defaultMountOptions{false, true, true}, nil
}
func TestProcessOptions(t *testing.T) {
tests := []struct {
name string
options []string
isTmpfs bool
sourcePath string
expected []string
expectErr bool
}{
{
name: "tmpfs",
options: []string{"rw", "size=512m"},
isTmpfs: true,
sourcePath: "",
expected: []string{"nodev", "nosuid", "rprivate", "rw", "size=512m", "tmpcopyup"},
},
{
name: "duplicate idmap option",
sourcePath: "/path/to/source",
options: []string{"idmap", "idmap"},
expectErr: true,
},
{
name: "mode allowed only with tmpfs",
sourcePath: "/path/to/source",
options: []string{"rw", "rbind", "mode=0123"},
expectErr: true,
},
{
name: "noswap allowed only with tmpfs",
sourcePath: "/path/to/source",
options: []string{"noswap"},
expectErr: true,
},
{
name: "tmpcopyup allowed only with tmpfs",
sourcePath: "/path/to/source",
options: []string{"tmpcopyup"},
expectErr: true,
},
{
name: "notmpcopyup allowed only with tmpfs",
sourcePath: "/path/to/source",
options: []string{"notmpcopyup"},
expectErr: true,
},
{
name: "z not allowed with tmpfs",
isTmpfs: true,
sourcePath: "/path/to/source",
options: []string{"z"},
expectErr: true,
},
{
name: "size allowed only with tmpfs",
sourcePath: "/path/to/source",
options: []string{"size=123456"},
expectErr: true,
},
{
name: "conflicting option dev/nodev",
sourcePath: "/path/to/source",
options: []string{"dev", "nodev"},
expectErr: true,
},
{
name: "conflicting option suid/nosuid",
sourcePath: "/path/to/source",
options: []string{"suid", "nosuid"},
expectErr: true,
},
{
name: "conflicting option exec/noexec",
sourcePath: "/path/to/source",
options: []string{"noexec", "exec"},
expectErr: true,
},
{
name: "conflicting option ro/rw",
sourcePath: "/path/to/source",
options: []string{"ro", "rw"},
expectErr: true,
},
{
name: "conflicting option bind/rbind",
sourcePath: "/path/to/source",
options: []string{"bind", "rbind"},
expectErr: true,
},
{
name: "conflicting option bind/rbind",
sourcePath: "/path/to/source",
options: []string{"bind", "rbind"},
expectErr: true,
},
{
name: "default bind mount",
sourcePath: "/path/to/source",
expected: []string{"nodev", "nosuid", "rbind", "rprivate", "rw"},
},
{
name: "default bind mount with bind",
sourcePath: "/path/to/source",
options: []string{"bind"},
expected: []string{"nodev", "nosuid", "bind", "private", "rw"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
opts, err := processOptionsInternal(tt.options, tt.isTmpfs, tt.sourcePath, getDefaultMountOptionsNoStat)
if tt.expectErr {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
sort.Strings(opts)
sort.Strings(tt.expected)
assert.Equal(t, opts, tt.expected)
}
})
}
}
func TestGetRootlessPauseProcessPidPath(t *testing.T) {
dir, err := GetRootlessPauseProcessPidPath()
assert.NoError(t, err)
assert.NotEqual(t, dir, "libpod/tmp/pause.pid")
}