unit test: Set image volume digest as part of container statuses

Signed-off-by: Itamar Holder <iholder@redhat.com>
This commit is contained in:
Itamar Holder
2025-10-22 10:18:31 +03:00
parent e299190b41
commit a4d9ac7bb0

View File

@@ -41,6 +41,7 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/version"
utilfeature "k8s.io/apiserver/pkg/util/feature"
core "k8s.io/client-go/testing"
@@ -4092,6 +4093,185 @@ func TestConvertToAPIContainerStatuses(t *testing.T) {
}
}
// imageDigestRuntime is a simple wrapper that returns a fixed digest for image volumes
type imageDigestRuntime struct {
*containertest.FakeRuntime
digest string
}
func (r *imageDigestRuntime) GetImageRef(ctx context.Context, image kubecontainer.ImageSpec) (string, error) {
return r.digest, nil
}
func TestConvertToAPIContainerStatusesWithImageVolumeDigest(t *testing.T) {
const (
imageVolumeName = "image-volume"
imageVolumeMountPath = "/mock/path"
imageVolumeRef = "registry.k8s.io/example:1.2.3"
imageDigest = "sha256:abcd1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd"
)
now := metav1.Now()
tests := []struct {
name string
pod *v1.Pod
currentStatus *kubecontainer.PodStatus
previousStatus []v1.ContainerStatus
expected []v1.ContainerStatus
}{
{
name: "with image volume - should include digest in status",
pod: &v1.Pod{
Spec: v1.PodSpec{
NodeName: "node123",
Containers: []v1.Container{
{
Name: "containerA",
VolumeMounts: []v1.VolumeMount{
{
Name: imageVolumeName,
MountPath: imageVolumeMountPath,
},
},
},
},
Volumes: []v1.Volume{
{
Name: imageVolumeName,
VolumeSource: v1.VolumeSource{
Image: &v1.ImageVolumeSource{
Reference: imageVolumeRef,
},
},
},
},
},
Status: v1.PodStatus{
ContainerStatuses: []v1.ContainerStatus{
runningState("containerA"),
},
},
ObjectMeta: metav1.ObjectMeta{Name: "my-pod", DeletionTimestamp: &now},
},
currentStatus: &kubecontainer.PodStatus{
ContainerStatuses: []*kubecontainer.Status{
{
ID: kubecontainer.ContainerID{Type: "test", ID: "containerA-id"},
Name: "containerA",
State: kubecontainer.ContainerStateRunning,
Image: imageVolumeRef,
Mounts: []kubecontainer.Mount{
{
Name: imageVolumeName,
ContainerPath: imageVolumeMountPath,
Image: &runtimeapi.ImageSpec{Image: imageVolumeRef},
},
},
},
},
},
previousStatus: []v1.ContainerStatus{
func() v1.ContainerStatus {
previousContainerA := runningState("containerA")
previousContainerA.VolumeMounts = []v1.VolumeMountStatus{
{
Name: imageVolumeName,
MountPath: imageVolumeMountPath,
},
}
return previousContainerA
}(),
},
expected: []v1.ContainerStatus{
func() v1.ContainerStatus {
containerStatus := runningState("containerA")
containerStatus.Image = imageVolumeRef
containerStatus.ContainerID = "test://containerA-id"
containerStatus.Resources = &v1.ResourceRequirements{}
containerStatus.VolumeMounts = []v1.VolumeMountStatus{
{
Name: imageVolumeName,
MountPath: imageVolumeMountPath,
VolumeStatus: v1.VolumeStatus{Image: &v1.ImageVolumeStatus{ImageRef: imageDigest}},
},
}
return containerStatus
}(),
},
},
{
name: "with no image volume - should not include digest in status",
pod: &v1.Pod{
Spec: v1.PodSpec{
NodeName: "node123",
Containers: []v1.Container{
{
Name: "containerA",
},
},
},
Status: v1.PodStatus{
ContainerStatuses: []v1.ContainerStatus{
runningState("containerA"),
},
},
ObjectMeta: metav1.ObjectMeta{Name: "my-pod", DeletionTimestamp: &now},
},
currentStatus: &kubecontainer.PodStatus{
ContainerStatuses: []*kubecontainer.Status{
{
ID: kubecontainer.ContainerID{Type: "test", ID: "containerA-id"},
Name: "containerA",
State: kubecontainer.ContainerStateRunning,
},
},
},
previousStatus: []v1.ContainerStatus{
runningState("containerA"),
},
expected: []v1.ContainerStatus{
func() v1.ContainerStatus {
containerStatus := runningState("containerA")
containerStatus.ContainerID = "test://containerA-id"
containerStatus.Resources = &v1.ResourceRequirements{}
return containerStatus
}(),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ImageVolume, true)
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ImageVolumeWithDigest, true)
testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */)
defer testKubelet.Cleanup()
kl := testKubelet.kubelet
// Mock the container runtime to return the expected image digest
kl.containerRuntime = &imageDigestRuntime{
FakeRuntime: &containertest.FakeRuntime{},
digest: imageDigest,
}
containerStatuses := kl.convertToAPIContainerStatuses(
test.pod,
test.currentStatus,
test.previousStatus,
test.pod.Spec.Containers,
sets.New(imageVolumeName),
false,
false,
false,
)
for i, status := range containerStatuses {
assert.Equal(t, test.expected[i], status, "[test %s]", test.name)
}
})
}
}
func Test_generateAPIPodStatus(t *testing.T) {
desiredState := v1.PodSpec{
NodeName: "machine",
@@ -5400,11 +5580,7 @@ func TestConvertToAPIContainerStatusesDataRace(t *testing.T) {
// detection, so would catch a race condition consistently, despite only spawning 2 goroutines
for i := 0; i < 2; i++ {
go func() {
<<<<<<< HEAD
kl.convertToAPIContainerStatuses(pod, criStatus, []v1.ContainerStatus{}, []v1.Container{}, false, false, false)
=======
kl.convertToAPIContainerStatuses(pod, criStatus, []v1.ContainerStatus{}, []v1.Container{}, nil, false, false)
>>>>>>> 3102f167ced (Set image volume digest as part of container statuses)
kl.convertToAPIContainerStatuses(pod, criStatus, []v1.ContainerStatus{}, []v1.Container{}, nil, false, false, false)
}()
}
}
@@ -5873,12 +6049,7 @@ func TestConvertToAPIContainerStatusesForResources(t *testing.T) {
state = tc.State
}
podStatus := testPodStatus(state, resources)
<<<<<<< HEAD
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, podStatus, []v1.ContainerStatus{tc.OldStatus}, tPod.Spec.Containers, false, false, false)
=======
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, podStatus, []v1.ContainerStatus{tc.OldStatus}, tPod.Spec.Containers, nil, false, false)
>>>>>>> 3102f167ced (Set image volume digest as part of container statuses)
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, podStatus, []v1.ContainerStatus{tc.OldStatus}, tPod.Spec.Containers, nil, false, false, false)
actual := cStatuses[0]
// Explicitly test AllocatedResources and Resources separately for debuggability.
assert.Equal(t, tc.Expected.AllocatedResources, actual.AllocatedResources, "AllocatedResources")
@@ -6004,11 +6175,9 @@ func TestConvertToAPIContainerStatusesForUser(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SupplementalGroupsPolicy, tc.featureEnabled)
tPod := testPod.DeepCopy()
t.Logf("TestCase: %q", tdesc)
<<<<<<< HEAD
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, tc.testPodStatus, tPod.Status.ContainerStatuses, tPod.Spec.Containers, false, false, false)
=======
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, tc.testPodStatus, tPod.Status.ContainerStatuses, tPod.Spec.Containers, nil, false, false)
>>>>>>> 3102f167ced (Set image volume digest as part of container statuses)
cStatuses := kubelet.convertToAPIContainerStatuses(tPod, tc.testPodStatus, tPod.Status.ContainerStatuses, tPod.Spec.Containers, nil, false, false, false)
assert.Equal(t, tc.expectedContainerStatus, cStatuses)
}
}