Skip to content

Commit

Permalink
kubelet: add GetHostIDsForPod()
Browse files Browse the repository at this point in the history
In future commits we will need this to set the user/group of supported
volumes of KEP 127 - Phase 1.

Signed-off-by: Rodrigo Campos <rodrigoca@microsoft.com>
  • Loading branch information
rata committed Aug 3, 2022
1 parent 9b2fc63 commit d07c268
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,10 @@ func (adc *attachDetachController) GetPodVolumeDir(podUID types.UID, pluginName,
return ""
}

func (adc *attachDetachController) GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return nil, nil, nil
}

func (adc *attachDetachController) GetPodPluginDir(podUID types.UID, pluginName string) string {
return ""
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/controller/volume/expand/expand_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,10 @@ func (expc *expandController) GetPodsDir() string {
return ""
}

func (expc *expandController) GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return nil, nil, nil
}

func (expc *expandController) GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
return ""
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/controller/volume/persistentvolume/volume_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ func (ctrl *PersistentVolumeController) GetPodVolumeDir(podUID types.UID, plugin
return ""
}

func (ctrl *PersistentVolumeController) GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return nil, nil, nil
}

func (ctrl *PersistentVolumeController) GetPodPluginDir(podUID types.UID, pluginName string) string {
return ""
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/kubelet/kubelet_pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,10 @@ func (kl *Kubelet) GetOrCreateUserNamespaceMappings(pod *v1.Pod) (*runtimeapi.Us
return kl.usernsManager.GetOrCreateUserNamespaceMappings(pod)
}

func (kl *Kubelet) getHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return kl.usernsManager.getHostIDsForPod(pod, containerUID, containerGID)
}

// GeneratePodHostNameAndDomain creates a hostname and domain name for a pod,
// given that pod's spec and annotations or returns an error.
func (kl *Kubelet) GeneratePodHostNameAndDomain(pod *v1.Pod) (string, string, error) {
Expand Down
65 changes: 65 additions & 0 deletions pkg/kubelet/userns_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,3 +528,68 @@ func (m *usernsManager) CleanupOrphanedPodUsernsAllocations(pods []*v1.Pod, runn

return nil
}

// getHostIDsForPod if the pod uses user namespaces, takes the uid and gid
// inside the container and returns the host UID and GID those are mapped to on
// the host. If containerUID/containerGID is nil, then it returns the host
// UID/GID for ID 0 inside the container.
// If the pod is not using user namespaces, as there is no mapping needed, the
// same containerUID and containerGID params are returned.
func (m *usernsManager) getHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.UserNamespacesStatelessPodsSupport) {
return containerUID, containerGID, nil
}

if pod == nil || pod.Spec.HostUsers == nil || *pod.Spec.HostUsers == true {
return containerUID, containerGID, nil
}

mapping, err := m.GetOrCreateUserNamespaceMappings(pod)
if err != nil {
err = fmt.Errorf("Error getting pod user namespace mapping: %w", err)
return
}

uid, err := hostIDFromMapping(mapping.Uids, containerUID)
if err != nil {
err = fmt.Errorf("Error getting host UID: %w", err)
return
}

gid, err := hostIDFromMapping(mapping.Gids, containerGID)
if err != nil {
err = fmt.Errorf("Error getting host GID: %w", err)
return
}

return &uid, &gid, nil
}

func hostIDFromMapping(mapping []*runtimeapi.IDMapping, containerId *int64) (int64, error) {
if len(mapping) == 0 {
return 0, fmt.Errorf("can't use empty user namespace mapping")
}

// If none is requested, root inside the container is used
id := int64(0)
if containerId != nil {
id = *containerId
}

for _, m := range mapping {
if m == nil {
continue
}

firstId := int64(m.ContainerId)
lastId := firstId + int64(m.Length) - 1

// The id we are looking for is in the range
if id >= firstId && id <= lastId {
// Return the host id for this container id
return int64(m.HostId) + id - firstId, nil
}
}

return 0, fmt.Errorf("ID: %v not present in pod user namespace", id)
}
131 changes: 131 additions & 0 deletions pkg/kubelet/userns_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
pkgfeatures "k8s.io/kubernetes/pkg/features"
)

Expand Down Expand Up @@ -176,6 +177,136 @@ func TestUserNsManagerParseUserNsFile(t *testing.T) {
}
}

func TestUserNsManagerHostIDFromMapping(t *testing.T) {
// mapping []*runtimeapi.IDMapping, containerId *int64

cases := []struct {
name string
success bool
containerId int64 // -1 means a nil ptr will be used.
expHostId int64
m []*runtimeapi.IDMapping
}{
{
name: "one basic mapping",
success: true,
containerId: -1,
expHostId: 0,
m: []*runtimeapi.IDMapping{
{
HostId: 0,
ContainerId: 0,
Length: userNsLength,
},
},
},
{
name: "one unprivileged mapping",
success: true,
containerId: -1,
expHostId: userNsLength * 2,
m: []*runtimeapi.IDMapping{
{
HostId: userNsLength * 2,
ContainerId: 0,
Length: userNsLength,
},
},
},
{
name: "one unprivileged mapping random id",
success: true,
containerId: 3,
expHostId: userNsLength*2 + 3,
m: []*runtimeapi.IDMapping{
{
HostId: userNsLength * 2,
ContainerId: 0,
Length: userNsLength,
},
},
},
{
name: "two unprivileged mapping",
success: true,
containerId: 0,
expHostId: userNsLength*2 + 0,
m: []*runtimeapi.IDMapping{
{
HostId: userNsLength * 2,
ContainerId: 0,
Length: 1,
},
{
HostId: userNsLength*2 + 10,
ContainerId: 1,
Length: 1,
},
},
},
{
name: "two unprivileged mapping - random id",
success: true,
containerId: 1,
expHostId: userNsLength*2 + 10,
m: []*runtimeapi.IDMapping{
{
HostId: userNsLength * 2,
ContainerId: 0,
Length: 1,
},
{
HostId: userNsLength*2 + 10,
ContainerId: 1,
Length: 1,
},
},
},
{
name: "two unprivileged mapping - not mapped user",
success: false,
containerId: 3,
m: []*runtimeapi.IDMapping{
{
HostId: userNsLength * 2,
ContainerId: 0,
Length: 1,
},
{
HostId: userNsLength*2 + 1,
ContainerId: 1,
Length: 1,
},
},
},
{
name: "no mappings",
success: false,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var containerId *int64
if tc.containerId != -1 {
containerId = &tc.containerId
}

id, err := hostIDFromMapping(tc.m, containerId)
if (tc.success && err != nil) || (!tc.success && err == nil) {
t.Fatalf("%v: expected success: %v - got error: %v", tc.name, tc.success, err)
}
if !tc.success && err != nil {
return
}

if id != tc.expHostId {
t.Errorf("expected: %v - got: %v", tc.expHostId, id)
}
})
}
}

func BenchmarkBitmaskFindAndSetFirstZero(t *testing.B) {
b := makeBitArray(userNsLength)
for i := 0; i < userNsLength; i++ {
Expand Down
10 changes: 10 additions & 0 deletions pkg/kubelet/volume_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ func (kvh *kubeletVolumeHost) GetPodsDir() string {
return kvh.kubelet.getPodsDir()
}

// GetHostIDsForPod if the pod uses user namespaces, takes the uid and gid
// inside the container and returns the host UID and GID those are mapped to on
// the host. If containerUID/containerGID is nil, then it returns the host
// UID/GID for ID 0 inside the container.
// If the pod is not using user namespaces, as there is no mapping needed, the
// same containerUID and containerGID params are returned.
func (kvh *kubeletVolumeHost) GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return kvh.kubelet.getHostIDsForPod(pod, containerUID, containerGID)
}

func (kvh *kubeletVolumeHost) GetPodVolumeDir(podUID types.UID, pluginName string, volumeName string) string {
dir := kvh.kubelet.getPodVolumeDir(podUID, pluginName, volumeName)
if runtime.GOOS == "windows" {
Expand Down
7 changes: 7 additions & 0 deletions pkg/volume/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,13 @@ type KubeletVolumeHost interface {
WaitForCacheSync() error
// Returns hostutil.HostUtils
GetHostUtil() hostutil.HostUtils
// GetHostIDsForPod if the pod uses user namespaces, takes the uid and
// gid inside the container and returns the host UID and GID those are
// mapped to on the host. If containerUID/containerGID is nil, then it
// returns the host UID/GID for ID 0 inside the container.
// If the pod is not using user namespaces, as there is no mapping needed, the
// same containerUID and containerGID params are returned.
GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error)
}

// AttachDetachVolumeHost is a AttachDetach Controller specific interface that plugins can use
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/testing/volume_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ func (f *fakeVolumeHost) GetPodsDir() string {
return filepath.Join(f.rootDir, "pods")
}

func (f *fakeVolumeHost) GetHostIDsForPod(pod *v1.Pod, containerUID, containerGID *int64) (hostUID, hostGID *int64, err error) {
return containerUID, containerGID, nil
}

func (f *fakeVolumeHost) GetPodVolumeDir(podUID types.UID, pluginName, volumeName string) string {
return filepath.Join(f.rootDir, "pods", string(podUID), "volumes", pluginName, volumeName)
}
Expand Down

0 comments on commit d07c268

Please sign in to comment.