-
Notifications
You must be signed in to change notification settings - Fork 510
OCPBUGS-87962: Add OCP-89593 test for boot image skew reset on reconcile-skip #6331
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?
Changes from all commits
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,7 @@ package extended | |||||||||||||||||||
|
|
||||||||||||||||||||
| import ( | ||||||||||||||||||||
| "context" | ||||||||||||||||||||
| "fmt" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| g "github.com/onsi/ginkgo/v2" | ||||||||||||||||||||
| o "github.com/onsi/gomega" | ||||||||||||||||||||
|
|
@@ -296,4 +297,65 @@ var _ = g.Describe("[sig-mco][Suite:openshift/machine-config-operator/disruptive | |||||||||||||||||||
| // Check machine-config CO upgradeable status, should be set to true | ||||||||||||||||||||
| o.Eventually(mcoCO, "1m", "10s").Should(BeUpgradeable(), "co/machine-config should be upgradeable when skew enforcement is disabled (None mode)") | ||||||||||||||||||||
| }) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // https://issues.redhat.com/browse/OCPBUGS-87962 | ||||||||||||||||||||
| g.It("[PolarionID:89593][OTP] Boot image skew check correctly reflects actual boot image version when MachineSets are reconcile-skipped [Disruptive]", g.Label("Platform:aws", "Platform:gce", "Platform:azure", "Platform:vsphere"), func() { | ||||||||||||||||||||
| skipTestIfSupportedPlatformNotMatched(oc, GCPPlatform, AWSPlatform, AzurePlatform, VspherePlatform) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| automaticOCPVersionPath := `{.status.bootImageSkewEnforcementStatus.automatic.ocpVersion}` | ||||||||||||||||||||
|
|
||||||||||||||||||||
| exutil.By("Ensure Automatic mode is active") | ||||||||||||||||||||
| o.Expect(machineConfiguration.RemoveSkew()).To(o.Succeed()) | ||||||||||||||||||||
| machineConfiguration.WaitForBootImageSkewEnforcementStatusMode(SkewEnforcementAutomaticMode) | ||||||||||||||||||||
| machineConfiguration.WaitForBootImageControllerComplete() | ||||||||||||||||||||
| logger.Infof("OK!\n") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| exutil.By("Get the automatic.ocpVersion before patch") | ||||||||||||||||||||
| originalOCPVersion, err := machineConfiguration.Get(automaticOCPVersionPath) | ||||||||||||||||||||
| o.Expect(err).NotTo(o.HaveOccurred(), "Error getting automatic.ocpVersion from %s", machineConfiguration) | ||||||||||||||||||||
| o.Expect(originalOCPVersion).NotTo(o.BeEmpty(), "automatic.ocpVersion should not be empty in Automatic mode") | ||||||||||||||||||||
| logger.Infof("automatic.ocpVersion before patch: %s", originalOCPVersion) | ||||||||||||||||||||
| logger.Infof("OK!\n") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| exutil.By("Pick a MachineSet and add an OwnerReference to trigger reconcile-skip") | ||||||||||||||||||||
| machineSetUnderTest := NewMachineSetList(oc.AsAdmin(), MachineAPINamespace).GetAllOrFail()[0] | ||||||||||||||||||||
| logger.Infof("MachineSet under test: %s", machineSetUnderTest.name) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| infraUID, err := oc.AsAdmin().WithoutNamespace().Run("get").Args( | ||||||||||||||||||||
| "infrastructure", "cluster", "-o", "jsonpath={.metadata.uid}", | ||||||||||||||||||||
| ).Output() | ||||||||||||||||||||
| o.Expect(err).NotTo(o.HaveOccurred(), "Failed to get Infrastructure UID") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| ownerRefPatch := fmt.Sprintf( | ||||||||||||||||||||
| `{"metadata":{"ownerReferences":[{"apiVersion":"config.openshift.io/v1","kind":"Infrastructure","name":"cluster","uid":"%s"}]}}`, | ||||||||||||||||||||
| infraUID) | ||||||||||||||||||||
| o.Expect(machineSetUnderTest.Patch("merge", ownerRefPatch)).To(o.Succeed()) | ||||||||||||||||||||
| logger.Infof("Added OwnerReference to MachineSet %s to trigger reconcile-skip", machineSetUnderTest.name) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| defer func() { | ||||||||||||||||||||
| exutil.By("Restoring MachineSet by removing OwnerReference") | ||||||||||||||||||||
| o.Expect(machineSetUnderTest.Patch("json", `[{"op":"remove","path":"/metadata/ownerReferences"}]`)).To(o.Succeed()) | ||||||||||||||||||||
| machineConfiguration.WaitForBootImageControllerComplete() | ||||||||||||||||||||
| }() | ||||||||||||||||||||
| logger.Infof("OK!\n") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| exutil.By("Wait for boot image controller to process and get automatic.ocpVersion after patch") | ||||||||||||||||||||
| machineConfiguration.WaitForBootImageControllerComplete() | ||||||||||||||||||||
| patchedOCPVersion, err := machineConfiguration.Get(automaticOCPVersionPath) | ||||||||||||||||||||
| o.Expect(err).NotTo(o.HaveOccurred(), "Error getting automatic.ocpVersion after patch") | ||||||||||||||||||||
| o.Expect(patchedOCPVersion).NotTo(o.BeEmpty(), "automatic.ocpVersion should not be empty after reconcile-skip") | ||||||||||||||||||||
| logger.Infof("automatic.ocpVersion after patch: %s", patchedOCPVersion) | ||||||||||||||||||||
| logger.Infof("OK!\n") | ||||||||||||||||||||
|
Comment on lines
+313
to
+348
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Assertion only checks non-emptiness, not that Per the controller contract ( Consider asserting that 🤖 Prompt for AI Agents
Comment on lines
+342
to
+348
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Inconsistent wait strategy vs. the restoration check below. This assertion uses a single synchronous 🤖 Prompt for AI Agents |
||||||||||||||||||||
|
|
||||||||||||||||||||
| exutil.By("Remove OwnerReference and verify automatic.ocpVersion is restored") | ||||||||||||||||||||
| o.Expect(machineSetUnderTest.Patch("json", `[{"op":"remove","path":"/metadata/ownerReferences"}]`)).To(o.Succeed()) | ||||||||||||||||||||
| machineConfiguration.WaitForBootImageControllerComplete() | ||||||||||||||||||||
| o.Eventually(func() string { | ||||||||||||||||||||
| v, _ := machineConfiguration.Get(automaticOCPVersionPath) | ||||||||||||||||||||
| return v | ||||||||||||||||||||
| }, "2m", "10s").Should(o.Equal(originalOCPVersion), | ||||||||||||||||||||
| "automatic.ocpVersion should be restored to %s after removing OwnerReference", originalOCPVersion) | ||||||||||||||||||||
|
Comment on lines
+353
to
+357
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Error from
♻️ Suggested simplification- o.Eventually(func() string {
- v, _ := machineConfiguration.Get(automaticOCPVersionPath)
- return v
- }, "2m", "10s").Should(o.Equal(originalOCPVersion),
+ o.Eventually(func() (string, error) {
+ return machineConfiguration.Get(automaticOCPVersionPath)
+ }, "2m", "10s").Should(o.Equal(originalOCPVersion),
"automatic.ocpVersion should be restored to %s after removing OwnerReference", originalOCPVersion)📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||
| logger.Infof("automatic.ocpVersion restored to: %s", originalOCPVersion) | ||||||||||||||||||||
| logger.Infof("OK!\n") | ||||||||||||||||||||
| }) | ||||||||||||||||||||
| }) | ||||||||||||||||||||
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.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does Kubernetes API server reject a JSON Patch (RFC 6902) "remove" operation when the target path no longer exists?💡 Result:
Yes, the Kubernetes API server rejects a JSON Patch (RFC 6902) remove operation if the target path does not exist [1][2]. According to the RFC 6902 specification, the target location for a "remove" operation must exist for the operation to be successful [3][4]. Because Kubernetes implements JSON Patch in accordance with this RFC, attempting to remove a non-existent path will result in a failure [1][5][2]. When such an error occurs, the API server rejects the request, typically returning an error (often a 422 Unprocessable Entity status code) [1]. This behavior is consistent with the standard's requirement that if any operation in a JSON Patch sequence fails, the entire patch is aborted [6][4]. If you need to avoid this error, you must ensure the path exists before attempting the removal, or handle the error on the client side [1][2]. Note that some specific Kubernetes-related tools or libraries may have their own wrapper implementations or configuration options (e.g., in admission controllers or specialized controllers) that might handle these operations differently, but the core Kubernetes API server enforces the strict RFC 6902 requirement [7][1][2].
Citations:
🏁 Script executed:
Repository: openshift/machine-config-operator
Length of output: 313
🏁 Script executed:
Repository: openshift/machine-config-operator
Length of output: 13527
Make the owner reference cleanup idempotent.
Line 351 removes
/metadata/ownerReferenceswith a JSON patch, and thedefercleanup repeats the same removal. A JSON Patchremoveon a path that no longer exists fails, so the deferred cleanup can make an otherwise passing spec fail.🐛 Proposed fix: make cleanup idempotent
📝 Committable suggestion
🤖 Prompt for AI Agents