-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Add automation case: ImageVolume should report kubelet image volume metrics correctly [OCP-84149] #30657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add automation case: ImageVolume should report kubelet image volume metrics correctly [OCP-84149] #30657
Changes from 2 commits
c7377ee
afbe65b
960dc98
4e02676
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,10 @@ package node | |||||
|
|
||||||
| import ( | ||||||
| "context" | ||||||
| "fmt" | ||||||
| "regexp" | ||||||
| "strconv" | ||||||
| "strings" | ||||||
| "time" | ||||||
|
|
||||||
| g "github.com/onsi/ginkgo/v2" | ||||||
|
|
@@ -87,6 +91,52 @@ var _ = g.Describe("[sig-node] [FeatureGate:ImageVolume] ImageVolume", func() { | |||||
| verifyVolumeMounted(f, pod2, "ls", "/mnt/image/bin/oc") | ||||||
| }) | ||||||
|
|
||||||
| g.It("should report kubelet image volume metrics correctly [OCP-84149]", func(ctx context.Context) { | ||||||
| const ( | ||||||
| podName = "image-volume-metrics-test" | ||||||
| imageRef = "quay.io/crio/artifact:v1" | ||||||
| mountPath = "/mnt/image" | ||||||
| ) | ||||||
|
|
||||||
| // Step 1: Create a pod with OCI image as volume source | ||||||
| g.By("Creating a pod with OCI image as volume source") | ||||||
| pod := buildPodWithImageVolume(f.Namespace.Name, "", podName, imageRef) | ||||||
| pod = createPodAndWaitForRunning(ctx, oc, pod) | ||||||
|
|
||||||
| // Step 2: Verify the image is mounted successfully and read-only | ||||||
| g.By("Verifying image volume is mounted into the container") | ||||||
| verifyImageVolumeMounted(f, pod, mountPath) | ||||||
|
|
||||||
| g.By("Verifying the mounted volume is read-only") | ||||||
| verifyVolumeReadOnly(f, pod, mountPath) | ||||||
|
|
||||||
| // Step 3: Check kubelet metrics about image volume | ||||||
| g.By("Checking kubelet metrics for image volume") | ||||||
| metrics, err := getKubeletMetrics(ctx, oc, pod.Spec.NodeName) | ||||||
| o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get kubelet metrics") | ||||||
|
|
||||||
| g.By("Verifying kubelet_image_volume_requested_total metric") | ||||||
| requestedTotal := parseMetricValue(metrics, "kubelet_image_volume_requested_total") | ||||||
| o.Expect(requestedTotal).To(o.BeNumerically(">=", 1), | ||||||
| "kubelet_image_volume_requested_total should be at least 1") | ||||||
| framework.Logf("kubelet_image_volume_requested_total: %d", requestedTotal) | ||||||
|
||||||
|
|
||||||
| g.By("Verifying kubelet_image_volume_mounted_succeed_total metric") | ||||||
| succeededTotal := parseMetricValue(metrics, "kubelet_image_volume_mounted_succeed_total") | ||||||
| o.Expect(succeededTotal).To(o.BeNumerically(">=", 1), | ||||||
| "kubelet_image_volume_mounted_succeed_total should be at least 1") | ||||||
| framework.Logf("kubelet_image_volume_mounted_succeed_total: %d", succeededTotal) | ||||||
|
|
||||||
| g.By("Verifying kubelet_image_volume_mounted_errors_total metric") | ||||||
| errorsTotal := parseMetricValue(metrics, "kubelet_image_volume_mounted_errors_total") | ||||||
| o.Expect(errorsTotal).To(o.Equal(0), | ||||||
| "kubelet_image_volume_mounted_errors_total should be 0") | ||||||
| framework.Logf("kubelet_image_volume_mounted_errors_total: %d", errorsTotal) | ||||||
|
|
||||||
| framework.Logf("All image volume metrics are reporting correctly") | ||||||
|
|
||||||
| }) | ||||||
|
|
||||||
| g.Context("when subPath is used", func() { | ||||||
| g.It("should handle image volume with subPath", func(ctx context.Context) { | ||||||
| pod := buildPodWithImageVolumeSubPath(f.Namespace.Name, "", podName, image, "bin") | ||||||
|
|
@@ -186,3 +236,93 @@ func buildPodWithMultipleImageVolumes(namespace, nodeName, podName, image1, imag | |||||
| }) | ||||||
| return pod | ||||||
| } | ||||||
|
|
||||||
| // verifyImageVolumeMounted verifies that the image volume is mounted and accessible | ||||||
| func verifyImageVolumeMounted(f *framework.Framework, pod *v1.Pod, mountPath string) { | ||||||
| g.By(fmt.Sprintf("Checking if volume is mounted at %s", mountPath)) | ||||||
|
|
||||||
| // Check if directory exists | ||||||
| stdout := e2epod.ExecCommandInContainer(f, pod.Name, pod.Spec.Containers[0].Name, | ||||||
| "ls", "-la", mountPath) | ||||||
| o.Expect(stdout).NotTo(o.BeEmpty(), "Mount path should contain files") | ||||||
| framework.Logf("Files in %s:\n%s", mountPath, stdout) | ||||||
|
|
||||||
| // Verify the expected file exists | ||||||
| stdout = e2epod.ExecCommandInContainer(f, pod.Name, pod.Spec.Containers[0].Name, | ||||||
| "ls", mountPath+"/file") | ||||||
| o.Expect(stdout).To(o.ContainSubstring("file"), "Expected file should exist") | ||||||
|
|
||||||
| // Verify file content | ||||||
| stdout = e2epod.ExecCommandInContainer(f, pod.Name, pod.Spec.Containers[0].Name, | ||||||
| "cat", mountPath+"/file") | ||||||
| o.Expect(stdout).To(o.Equal("2"), "File content should be '1'") | ||||||
|
||||||
| o.Expect(stdout).To(o.Equal("2"), "File content should be '1'") | |
| o.Expect(stdout).To(o.Equal("2"), "File content should be '2'") |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit(non blocking): I think only the last check is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, it's ok to only keep the last check.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Returning 0 when a metric isn't found could lead to misleading test results. Consider returning an error or using a tuple return (int, bool) to distinguish between "metric = 0" and "metric not found".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good suggestion, I will use (int, bool) as return type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we test for an exact count here? Same for the 2 other cases below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it intends to check if there's requested_total_count exists and is calculated correctly, not to check the exact count.