270 lines
8.0 KiB
Go
270 lines
8.0 KiB
Go
//go:build linux
|
|
|
|
package storage
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/containers/storage/pkg/idtools"
|
|
)
|
|
|
|
func TestGetAutoUserNSMapping(t *testing.T) {
|
|
type args struct {
|
|
size int
|
|
availableUIDs *idSet
|
|
availableGIDs *idSet
|
|
usedUIDMappings []idtools.IDMap
|
|
usedGIDMappings []idtools.IDMap
|
|
additionalUIDMappings []idtools.IDMap
|
|
additionalGIDMappings []idtools.IDMap
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantUIDMappings []idtools.IDMap
|
|
wantGIDMappings []idtools.IDMap
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Normal",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 100000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 100000}}),
|
|
},
|
|
wantUIDMappings: []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}},
|
|
wantGIDMappings: []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 65536}},
|
|
},
|
|
{
|
|
name: "NotEnoughAvailableUIDs",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 10000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 100000}}),
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "NotEnoughAvailableGIDs",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 100000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 10000}}),
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "WithUsedIDs",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 100000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 100000}}),
|
|
usedUIDMappings: []idtools.IDMap{{ContainerID: 0, HostID: 2000, Size: 10000}},
|
|
usedGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1000, Size: 10000},
|
|
{ContainerID: 10000, HostID: 20000, Size: 5000},
|
|
{ContainerID: 15000, HostID: 30000, Size: 5000},
|
|
},
|
|
},
|
|
wantUIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1000, Size: 1000},
|
|
{ContainerID: 1000, HostID: 12000, Size: 65536 - 1000},
|
|
},
|
|
wantGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 11000, Size: 9000},
|
|
{ContainerID: 9000, HostID: 25000, Size: 5000},
|
|
{ContainerID: 14000, HostID: 35000, Size: 65536 - 9000 - 5000},
|
|
},
|
|
},
|
|
{
|
|
name: "WithUsedAndAdditionalIDs",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 100000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 100000}}),
|
|
usedUIDMappings: []idtools.IDMap{{ContainerID: 0, HostID: 2000, Size: 10000}},
|
|
usedGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1000, Size: 10000},
|
|
{ContainerID: 10000, HostID: 20000, Size: 5000},
|
|
{ContainerID: 15000, HostID: 30000, Size: 5000},
|
|
},
|
|
additionalUIDMappings: []idtools.IDMap{{ContainerID: 0, HostID: 1000, Size: 1}},
|
|
additionalGIDMappings: []idtools.IDMap{{ContainerID: 1, HostID: 1001, Size: 1}},
|
|
},
|
|
wantUIDMappings: []idtools.IDMap{
|
|
{ContainerID: 1, HostID: 1001, Size: 999},
|
|
{ContainerID: 1000, HostID: 12000, Size: 65535 - 999},
|
|
{ContainerID: 0, HostID: 1000, Size: 1},
|
|
},
|
|
wantGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 11000, Size: 1},
|
|
{ContainerID: 2, HostID: 11001, Size: 8999},
|
|
{ContainerID: 9001, HostID: 25000, Size: 5000},
|
|
{ContainerID: 14001, HostID: 35000, Size: 65535 - 1 - 8999 - 5000},
|
|
{ContainerID: 1, HostID: 1001, Size: 1},
|
|
},
|
|
},
|
|
{
|
|
name: "DiscontinuedAvailableIntervals",
|
|
args: args{
|
|
size: 65536,
|
|
availableUIDs: newIDSet([]interval{{1000, 50000}, {80000, 130000}}),
|
|
availableGIDs: newIDSet([]interval{{1000, 30000}, {70000, 90000}, {110000, 160000}}),
|
|
usedUIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 2000, Size: 10000},
|
|
{ContainerID: 0, HostID: 10000, Size: 10000},
|
|
{ContainerID: 0, HostID: 40000, Size: 10000},
|
|
},
|
|
usedGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1000, Size: 10000},
|
|
{ContainerID: 100, HostID: 20000, Size: 5000},
|
|
{ContainerID: 150, HostID: 30000, Size: 5000},
|
|
},
|
|
additionalUIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1002, Size: 1},
|
|
},
|
|
additionalGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 0, HostID: 1003, Size: 1},
|
|
{ContainerID: 1, HostID: 1001, Size: 1},
|
|
{ContainerID: 2, HostID: 1006, Size: 1},
|
|
{ContainerID: 100, HostID: 1100, Size: 10},
|
|
},
|
|
},
|
|
wantUIDMappings: []idtools.IDMap{
|
|
{ContainerID: 1, HostID: 1000, Size: 2},
|
|
{ContainerID: 3, HostID: 1003, Size: 997},
|
|
{ContainerID: 1000, HostID: 20000, Size: 20000},
|
|
{ContainerID: 21000, HostID: 80000, Size: 65535 - 2 - 997 - 20000},
|
|
{ContainerID: 0, HostID: 1002, Size: 1},
|
|
},
|
|
wantGIDMappings: []idtools.IDMap{
|
|
{ContainerID: 3, HostID: 11000, Size: 97},
|
|
{ContainerID: 110, HostID: 11000 + 97, Size: /*9000-97=*/ 8903},
|
|
{ContainerID: /*110+8903=*/ 9013, HostID: 25000, Size: 5000},
|
|
{ContainerID: /*9013+5000=*/ 14013, HostID: 70000, Size: 20000},
|
|
{ContainerID: /*14013+20000*/ 34013, HostID: 110000, Size: 65536 - 13 - 97 - 8903 - 5000 - 20000},
|
|
{ContainerID: 0, HostID: 1003, Size: 1},
|
|
{ContainerID: 1, HostID: 1001, Size: 1},
|
|
{ContainerID: 2, HostID: 1006, Size: 1},
|
|
{ContainerID: 100, HostID: 1100, Size: 10},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gotUIDMappings, gotGIDMappings, err := getAutoUserNSIDMappings(
|
|
tt.args.size,
|
|
tt.args.availableUIDs, tt.args.availableGIDs,
|
|
tt.args.usedUIDMappings, tt.args.usedGIDMappings,
|
|
tt.args.additionalUIDMappings, tt.args.additionalGIDMappings,
|
|
)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("getAutoUserNSMapping() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(gotUIDMappings, tt.wantUIDMappings) {
|
|
t.Errorf("getAutoUserNSMapping() gotUIDMappings = %v, want %v", gotUIDMappings, tt.wantUIDMappings)
|
|
}
|
|
if !reflect.DeepEqual(gotGIDMappings, tt.wantGIDMappings) {
|
|
t.Errorf("getAutoUserNSMapping() gotGIDMappings = %v, want %v", gotGIDMappings, tt.wantGIDMappings)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseMountedFiles(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
passwdContent string
|
|
groupContent string
|
|
expectedMax uint32
|
|
}{
|
|
{
|
|
name: "basic case",
|
|
passwdContent: `
|
|
root:x:0:0:root:/root:/bin/bash
|
|
user1:x:1000:1000::/home/user1:/bin/bash
|
|
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin`,
|
|
groupContent: `
|
|
root:x:0:
|
|
user1:x:1000:
|
|
nogroup:x:65534:`,
|
|
expectedMax: 1001,
|
|
},
|
|
{
|
|
name: "only passwd",
|
|
passwdContent: `
|
|
root:x:0:0:root:/root:/bin/bash
|
|
user1:x:4001:4001::/home/user1:/bin/bash
|
|
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin`,
|
|
groupContent: "",
|
|
expectedMax: 4002,
|
|
},
|
|
{
|
|
name: "only groups",
|
|
passwdContent: "",
|
|
groupContent: `
|
|
root:x:0:
|
|
admin:x:3000:
|
|
nobody:x:65534:`,
|
|
expectedMax: 3001,
|
|
},
|
|
{
|
|
name: "empty files",
|
|
passwdContent: "",
|
|
groupContent: "",
|
|
expectedMax: 0,
|
|
},
|
|
{
|
|
name: "invalid passwd file",
|
|
passwdContent: "FOOBAR",
|
|
groupContent: "",
|
|
expectedMax: 0,
|
|
},
|
|
{
|
|
name: "invalid groups file",
|
|
passwdContent: "",
|
|
groupContent: "FOOBAR",
|
|
expectedMax: 0,
|
|
},
|
|
{
|
|
name: "nogroup ignored",
|
|
passwdContent: "",
|
|
groupContent: `
|
|
root:x:0:
|
|
admin:x:4000:
|
|
nogroup:x:65533:`,
|
|
expectedMax: 4001,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "containermount")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create temp dir: %v", err)
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
passwdFile := filepath.Join(tmpDir, "passwd")
|
|
if err := os.WriteFile(passwdFile, []byte(tt.passwdContent), 0o644); err != nil {
|
|
t.Fatalf("Failed to write passwd file: %v", err)
|
|
}
|
|
|
|
groupFile := filepath.Join(tmpDir, "group")
|
|
if err := os.WriteFile(groupFile, []byte(tt.groupContent), 0o644); err != nil {
|
|
t.Fatalf("Failed to write group file: %v", err)
|
|
}
|
|
|
|
result := parseMountedFiles(tmpDir, passwdFile, groupFile)
|
|
|
|
if result != tt.expectedMax {
|
|
t.Errorf("Expected max %d, but got %d", tt.expectedMax, result)
|
|
}
|
|
})
|
|
}
|
|
}
|