mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	kubelet: add GetHostIDsForPod()
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>
This commit is contained in:
		@@ -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 ""
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ""
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -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++ {
 | 
			
		||||
 
 | 
			
		||||
@@ -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" {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user