Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion pkg/cdi/container-edits.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,18 @@ func (e *ContainerEdits) Apply(spec *oci.Spec) error {
}

if len(e.Mounts) > 0 {
var (
uids []oci.LinuxIDMapping
gids []oci.LinuxIDMapping
)

if specHasUserNamespace(spec) {
uids = cloneIDMappings(spec.Linux.UIDMappings)
gids = cloneIDMappings(spec.Linux.GIDMappings)
}
for _, m := range e.Mounts {
specgen.RemoveMount(m.ContainerPath)
specgen.AddMount((&Mount{m}).toOCI())
specgen.AddMount((&Mount{m}).toOCI(withMountIDMappings(uids, gids)))
}
sortMounts(&specgen)
}
Expand Down Expand Up @@ -389,3 +398,26 @@ func (m orderedMounts) Swap(i, j int) {
func (m orderedMounts) parts(i int) int {
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
}

// specHasUserNamespace returns true if the OCI Spec has a Linux UserNamespace.
func specHasUserNamespace(spec *oci.Spec) bool {
if spec == nil || spec.Linux == nil {
return false
}
for _, ns := range spec.Linux.Namespaces {
if ns.Type == oci.UserNamespace {
return true
}
}
return false
}

// cloneIDMappings clones a slice of OCI LinuxIDMappings.
func cloneIDMappings(mappings []oci.LinuxIDMapping) []oci.LinuxIDMapping {
if mappings == nil {
return nil
}
clone := make([]oci.LinuxIDMapping, len(mappings))
copy(clone, mappings)
return clone
}
97 changes: 97 additions & 0 deletions pkg/cdi/container-edits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,103 @@ func TestApplyContainerEdits(t *testing.T) {
},
},
},
{
name: "mount added to container with Linux user namespace and uid/gid mappings",
spec: &oci.Spec{
Linux: &oci.Linux{
UIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
HostID: 1000,
Size: 999,
},
},
GIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
HostID: 2000,
Size: 777,
},
},
Namespaces: []oci.LinuxNamespace{
{
Type: oci.UserNamespace,
Path: "/foo/bar",
},
},
},
Mounts: []oci.Mount{
{
Source: "/some/host/path1",
Destination: "/dest/path/c",
},
{
Source: "/some/host/path2",
Destination: "/dest/path/b",
},
},
},
edits: &cdi.ContainerEdits{
Mounts: []*cdi.Mount{
{
HostPath: "/some/host/path3",
ContainerPath: "/dest/path/a",
},
},
},
result: &oci.Spec{
Linux: &oci.Linux{
UIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
HostID: 1000,
Size: 999,
},
},
GIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
HostID: 2000,
Size: 777,
},
},
Namespaces: []oci.LinuxNamespace{
{
Type: oci.UserNamespace,
Path: "/foo/bar",
},
},
},
Mounts: []oci.Mount{
{
Source: "/some/host/path1",
Destination: "/dest/path/c",
},
{
Source: "/some/host/path2",
Destination: "/dest/path/b",
},
{
Source: "/some/host/path3",
Destination: "/dest/path/a",
UIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is ContainerID non-zero?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean in practice ? In principle, one could set up arbitrary mappings with multiple ranges which is possible, but in practice I think you'll only see a single range and starting (in the container) with 0, because that's what this is primarily used for. But from the OCI Spec point of view, nothing dictates any range or sub-range to start (in the container) at 0.

HostID: 1000,
Size: 999,
},
},
GIDMappings: []oci.LinuxIDMapping{
{
ContainerID: 0,
HostID: 2000,
Size: 777,
},
},
},
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
edits := ContainerEdits{tc.edits}
Expand Down
23 changes: 21 additions & 2 deletions pkg/cdi/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,33 @@ func (h *Hook) toOCI() spec.Hook {
}
}

// Additional OCI mount option to apply to injected mounts.
type ociMountOption func(*spec.Mount)

// withMountIDMappings adds UID and GID mappings for the given mount.
func withMountIDMappings(uid, gid []spec.LinuxIDMapping) ociMountOption {
return func(m *spec.Mount) {
if uid != nil {
m.UIDMappings = uid
}
if gid != nil {
m.GIDMappings = gid
}
}
}

// toOCI returns the opencontainers runtime Spec Mount for this Mount.
func (m *Mount) toOCI() spec.Mount {
return spec.Mount{
func (m *Mount) toOCI(options ...ociMountOption) spec.Mount {
om := spec.Mount{
Source: m.HostPath,
Destination: m.ContainerPath,
Options: m.Options,
Type: m.Type,
}
for _, o := range options {
o(&om)
}
return om
}

// toOCI returns the opencontainers runtime Spec LinuxDevice for this DeviceNode.
Expand Down