Files
podman/libpod/container_internal_test.go
Giuseppe Scrivano 96af9e4e0f libpod: support relative positions for idmaps
we were previously using an experimental feature in crun, but we lost
this capability once we moved to using the OCI runtime spec to specify
the volume mappings in fdcc2257df0fb0cb72d3fbe1b5aa8625955e1219.

Add the same feature to libpod, so that we can support relative
positions for the idmaps.

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

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-02-16 10:33:51 +01:00

201 lines
5.0 KiB
Go

package libpod
import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
"github.com/containers/storage/pkg/idtools"
stypes "github.com/containers/storage/types"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
// hookPath is the path to an example hook executable.
var hookPath string
func TestParseOptionIDs(t *testing.T) {
idMap := []idtools.IDMap{
{
ContainerID: 0,
HostID: 1,
Size: 10000,
},
}
_, err := parseOptionIDs(idMap, "uids=100-200-2")
assert.NotNil(t, err)
mappings, err := parseOptionIDs(idMap, "100-200-2")
assert.Nil(t, err)
assert.NotNil(t, mappings)
assert.Equal(t, len(mappings), 1)
assert.Equal(t, mappings[0].ContainerID, 100)
assert.Equal(t, mappings[0].HostID, 200)
assert.Equal(t, mappings[0].Size, 2)
mappings, err = parseOptionIDs(idMap, "100-200-2#300-400-5")
assert.Nil(t, err)
assert.NotNil(t, mappings)
assert.Equal(t, len(mappings), 2)
assert.Equal(t, mappings[0].ContainerID, 100)
assert.Equal(t, mappings[0].HostID, 200)
assert.Equal(t, mappings[0].Size, 2)
assert.Equal(t, mappings[1].ContainerID, 300)
assert.Equal(t, mappings[1].HostID, 400)
assert.Equal(t, mappings[1].Size, 5)
mappings, err = parseOptionIDs(idMap, "@100-200-2#@300-400-5")
assert.Nil(t, err)
assert.NotNil(t, mappings)
assert.Equal(t, len(mappings), 2)
assert.Equal(t, mappings[0].ContainerID, 100)
assert.Equal(t, mappings[0].HostID, 201)
assert.Equal(t, mappings[0].Size, 2)
assert.Equal(t, mappings[1].ContainerID, 300)
assert.Equal(t, mappings[1].HostID, 401)
assert.Equal(t, mappings[1].Size, 5)
_, err = parseOptionIDs(idMap, "@10000-20000-2")
assert.NotNil(t, err)
}
func TestParseIDMapMountOption(t *testing.T) {
uidMap := []idtools.IDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 10000,
},
}
gidMap := []idtools.IDMap{
{
ContainerID: 0,
HostID: 2000,
Size: 10000,
},
}
options := stypes.IDMappingOptions{
UIDMap: uidMap,
GIDMap: gidMap,
}
uids, gids, err := parseIDMapMountOption(options, "idmap", true)
assert.Nil(t, err)
assert.Equal(t, len(uids), 1)
assert.Equal(t, len(gids), 1)
assert.Equal(t, uids[0].ContainerID, uint32(1000))
assert.Equal(t, uids[0].HostID, uint32(0))
assert.Equal(t, uids[0].Size, uint32(10000))
assert.Equal(t, gids[0].ContainerID, uint32(2000))
assert.Equal(t, gids[0].HostID, uint32(0))
assert.Equal(t, gids[0].Size, uint32(10000))
uids, gids, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10", true)
assert.Nil(t, err)
assert.Equal(t, len(uids), 2)
assert.Equal(t, len(gids), 1)
assert.Equal(t, uids[0].ContainerID, uint32(1))
assert.Equal(t, uids[0].HostID, uint32(0))
assert.Equal(t, uids[0].Size, uint32(10))
assert.Equal(t, uids[1].ContainerID, uint32(11))
assert.Equal(t, uids[1].HostID, uint32(10))
assert.Equal(t, uids[1].Size, uint32(10))
assert.Equal(t, gids[0].ContainerID, uint32(3))
assert.Equal(t, gids[0].HostID, uint32(0))
assert.Equal(t, gids[0].Size, uint32(10))
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10;foobar=bar", true)
assert.NotNil(t, err)
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12", true)
assert.NotNil(t, err)
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0-12--12", true)
assert.NotNil(t, err)
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#-1-12-12", true)
assert.NotNil(t, err)
_, _, err = parseIDMapMountOption(options, "idmap=uids=0-1-10#10-11-10;gids=0-3-10#0--12-0", true)
assert.NotNil(t, err)
}
func TestPostDeleteHooks(t *testing.T) {
ctx := context.Background()
dir := t.TempDir()
statePath := filepath.Join(dir, "state")
copyPath := filepath.Join(dir, "copy")
c := Container{
runtime: &Runtime{},
config: &ContainerConfig{
ID: "123abc",
Spec: &rspec.Spec{
Annotations: map[string]string{
"a": "b",
},
},
ContainerRootFSConfig: ContainerRootFSConfig{
StaticDir: dir, // not the bundle, but good enough for this test
},
},
state: &ContainerState{
ExtensionStageHooks: map[string][]rspec.Hook{
"poststop": {
rspec.Hook{
Path: hookPath,
Args: []string{"sh", "-c", fmt.Sprintf("cat >%s", statePath)},
},
rspec.Hook{
Path: "/does/not/exist",
},
rspec.Hook{
Path: hookPath,
Args: []string{"sh", "-c", fmt.Sprintf("cp %s %s", statePath, copyPath)},
},
},
},
},
}
err := c.postDeleteHooks(ctx)
if err != nil {
t.Fatal(err)
}
stateRegexp := `{"ociVersion":"1\.0\.2-dev","id":"123abc","status":"stopped","bundle":"` + dir + `","annotations":{"a":"b"}}`
for _, p := range []string{statePath, copyPath} {
path := p
t.Run(path, func(t *testing.T) {
content, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
assert.Regexp(t, stateRegexp, string(content))
})
}
}
func init() {
if runtime.GOOS != "windows" {
hookPath = "/bin/sh"
} else {
panic("we need a reliable executable path on Windows")
}
}