issue 8960: implement PodVolume exposer for PVB/PVR

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
Lyndon-Li
2025-05-29 17:06:20 +08:00
parent f3685f37f6
commit b7d997130d
13 changed files with 1333 additions and 49 deletions

View File

@@ -41,6 +41,12 @@ const (
// nodeAgentRole marks pods with node-agent role on all nodes.
nodeAgentRole = "node-agent"
// hostPodVolume is the name of the volume in node-agent for host-pod mount
hostPodVolume = "host-pods"
// HostPodVolumeMountPoint is the mount point of the volume in node-agent for host-pod mount
HostPodVolumeMountPoint = "host_pods"
)
var (
@@ -249,3 +255,37 @@ func GetAnnotationValue(ctx context.Context, kubeClient kubernetes.Interface, na
return val, nil
}
func GetHostPodPath(ctx context.Context, kubeClient kubernetes.Interface, namespace string, osType string) (string, error) {
dsName := daemonSet
if osType == kube.NodeOSWindows {
dsName = daemonsetWindows
}
ds, err := kubeClient.AppsV1().DaemonSets(namespace).Get(ctx, dsName, metav1.GetOptions{})
if err != nil {
return "", errors.Wrapf(err, "error getting daemonset %s", dsName)
}
var volume *corev1api.Volume
for _, v := range ds.Spec.Template.Spec.Volumes {
if v.Name == hostPodVolume {
volume = &v
break
}
}
if volume == nil {
return "", errors.New("host pod volume is not found")
}
if volume.HostPath == nil {
return "", errors.New("host pod volume is not a host path volume")
}
if volume.HostPath.Path == "" {
return "", errors.New("host pod volume path is empty")
}
return volume.HostPath.Path, nil
}

View File

@@ -590,3 +590,164 @@ func TestGetAnnotationValue(t *testing.T) {
})
}
}
func TestGetHostPodPath(t *testing.T) {
daemonSet := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "node-agent",
},
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
},
}
daemonSetWithHostPodVolume := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "node-agent",
},
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
},
Spec: appsv1api.DaemonSetSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{
Name: hostPodVolume,
},
},
},
},
},
}
daemonSetWithHostPodVolumeAndEmptyPath := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "node-agent",
},
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
},
Spec: appsv1api.DaemonSetSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{
Name: hostPodVolume,
VolumeSource: corev1api.VolumeSource{
HostPath: &corev1api.HostPathVolumeSource{},
},
},
},
},
},
},
}
daemonSetWithHostPodVolumeAndValidPath := &appsv1api.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "node-agent",
},
TypeMeta: metav1.TypeMeta{
Kind: "DaemonSet",
},
Spec: appsv1api.DaemonSetSpec{
Template: corev1api.PodTemplateSpec{
Spec: corev1api.PodSpec{
Volumes: []corev1api.Volume{
{
Name: hostPodVolume,
VolumeSource: corev1api.VolumeSource{
HostPath: &corev1api.HostPathVolumeSource{
Path: "/var/lib/kubelet/pods",
},
},
},
},
},
},
},
}
tests := []struct {
name string
kubeClientObj []runtime.Object
namespace string
osType string
expectedValue string
expectErr string
}{
{
name: "ds get error",
namespace: "fake-ns",
osType: kube.NodeOSWindows,
kubeClientObj: []runtime.Object{
daemonSet,
},
expectErr: "error getting daemonset node-agent-windows: daemonsets.apps \"node-agent-windows\" not found",
},
{
name: "no host pod volume",
namespace: "fake-ns",
osType: kube.NodeOSLinux,
kubeClientObj: []runtime.Object{
daemonSet,
},
expectErr: "host pod volume is not found",
},
{
name: "no host pod volume path",
namespace: "fake-ns",
osType: kube.NodeOSLinux,
kubeClientObj: []runtime.Object{
daemonSetWithHostPodVolume,
},
expectErr: "host pod volume is not a host path volume",
},
{
name: "empty host pod volume path",
namespace: "fake-ns",
osType: kube.NodeOSLinux,
kubeClientObj: []runtime.Object{
daemonSetWithHostPodVolumeAndEmptyPath,
},
expectErr: "host pod volume path is empty",
},
{
name: "succeed",
namespace: "fake-ns",
osType: kube.NodeOSLinux,
kubeClientObj: []runtime.Object{
daemonSetWithHostPodVolumeAndValidPath,
},
expectedValue: "/var/lib/kubelet/pods",
},
{
name: "succeed on empty os type",
namespace: "fake-ns",
kubeClientObj: []runtime.Object{
daemonSetWithHostPodVolumeAndValidPath,
},
expectedValue: "/var/lib/kubelet/pods",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
fakeKubeClient := fake.NewSimpleClientset(test.kubeClientObj...)
path, err := GetHostPodPath(context.TODO(), fakeKubeClient, test.namespace, test.osType)
if test.expectErr == "" {
assert.NoError(t, err)
assert.Equal(t, test.expectedValue, path)
} else {
assert.EqualError(t, err, test.expectErr)
}
})
}
}