|
| 1 | +package extended |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + |
| 7 | + osconfigv1 "github.com/openshift/api/config/v1" |
| 8 | + "sigs.k8s.io/yaml" |
| 9 | + |
| 10 | + machinev1 "github.com/openshift/api/machine/v1" |
| 11 | + machinev1beta1 "github.com/openshift/api/machine/v1beta1" |
| 12 | + exutil "github.com/openshift/machine-config-operator/test/extended/util" |
| 13 | + |
| 14 | + o "github.com/onsi/gomega" |
| 15 | + |
| 16 | + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| 17 | + e2e "k8s.io/kubernetes/test/e2e/framework" |
| 18 | +) |
| 19 | + |
| 20 | +// verifyControlPlaneMachineSetUpdate verifies that the the boot image values of a ControlPlaneMachineSet are reconciled correctly |
| 21 | +// nolint:dupl // I separated these from verifyMachineSetUpdate for readability |
| 22 | +func verifyControlPlaneMachineSetUpdate(oc *exutil.CLI, cpms machinev1.ControlPlaneMachineSet, updateExpected bool) { |
| 23 | + |
| 24 | + newProviderSpecPatch, originalProviderSpecPatch, backdatedBootImage, originalBootImage := createFakeUpdatePatchCPMS(oc, cpms) |
| 25 | + err := oc.Run("patch").Args(ControlPlaneMachinesetQualifiedName, cpms.Name, "-p", newProviderSpecPatch, "-n", MAPINamespace, "--type=json").Execute() |
| 26 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 27 | + defer func() { |
| 28 | + // Restore machineSet to original boot image as the machineset may be used by other test variants, regardless of success/fail |
| 29 | + err = oc.Run("patch").Args(ControlPlaneMachinesetQualifiedName, cpms.Name, "-p", originalProviderSpecPatch, "-n", MAPINamespace, "--type=json").Execute() |
| 30 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 31 | + e2e.Logf("Restored build name in the machineset %s to \"%s\"", cpms.Name, originalBootImage) |
| 32 | + }() |
| 33 | + // Ensure boot image controller is not progressing |
| 34 | + e2e.Logf("Waiting until the boot image controller is not progressing...") |
| 35 | + waitForBootImageControllerToComplete(oc) |
| 36 | + |
| 37 | + // Fetch the providerSpec of the machineset under test again |
| 38 | + providerSpec, err := oc.Run("get").Args(ControlPlaneMachinesetQualifiedName, cpms.Name, "-o", "template", "--template=`{{.spec.template.machines_v1beta1_machine_openshift_io.spec.providerSpec.value}}`", "-n", MAPINamespace).Output() |
| 39 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 40 | + |
| 41 | + // Verify that the machineset has the expected boot image values |
| 42 | + // If an update is expected, the backdated boot image should not be present |
| 43 | + // If an update is NOT expected, the backdated boot image should still be present; ie machineset is left untouched |
| 44 | + if updateExpected { |
| 45 | + o.Expect(providerSpec).ShouldNot(o.ContainSubstring(backdatedBootImage)) |
| 46 | + } else { |
| 47 | + o.Expect(providerSpec).Should(o.ContainSubstring(backdatedBootImage)) |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +// createFakeUpdatePatchCPMS creates an update patch for the ControlPlaneMachineSet object based on the platform |
| 52 | +func createFakeUpdatePatchCPMS(oc *exutil.CLI, cpms machinev1.ControlPlaneMachineSet) (string, string, string, string) { |
| 53 | + infra, err := oc.AdminConfigClient().ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{}) |
| 54 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 55 | + |
| 56 | + switch infra.Status.PlatformStatus.Type { |
| 57 | + case osconfigv1.AWSPlatformType: |
| 58 | + return generateAWSProviderSpecPatchCPMS(cpms) |
| 59 | + case osconfigv1.GCPPlatformType: |
| 60 | + return generateGCPProviderSpecPatchCPMS(cpms) |
| 61 | + case osconfigv1.AzurePlatformType: |
| 62 | + return generateAzureProviderSpecPatchCPMS(cpms) |
| 63 | + default: |
| 64 | + e2e.Failf("unexpected platform type; should not be here") |
| 65 | + return "", "", "", "" |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +// generateAWSProviderSpecPatchCPMS generates a fake update patch for the AWS ControlPlaneMachineSet |
| 70 | +func generateAWSProviderSpecPatchCPMS(cpms machinev1.ControlPlaneMachineSet) (string, string, string, string) { |
| 71 | + providerSpec := new(machinev1beta1.AWSMachineProviderConfig) |
| 72 | + err := unmarshalProviderSpecCPMS(&cpms, providerSpec) |
| 73 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 74 | + |
| 75 | + // Modify the boot image to an older known AMI value |
| 76 | + // See: https://issues.redhat.com/browse/OCPBUGS-57426 |
| 77 | + originalBootImage := *providerSpec.AMI.ID |
| 78 | + newBootImage := "ami-000145e5a91e9ac22" |
| 79 | + jsonPatch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/ami/id", "value": "%s"}]`, newBootImage) |
| 80 | + |
| 81 | + // Create JSON patch to restore original AMI ID |
| 82 | + originalJSONPatch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/ami/id", "value": "%s"}]`, originalBootImage) |
| 83 | + |
| 84 | + return jsonPatch, originalJSONPatch, newBootImage, originalBootImage |
| 85 | + |
| 86 | +} |
| 87 | + |
| 88 | +// generateGCPProviderSpecPatchCPMS generates a fake update patch for the GCP ControlPlaneMachineSet |
| 89 | +func generateGCPProviderSpecPatchCPMS(cpms machinev1.ControlPlaneMachineSet) (string, string, string, string) { |
| 90 | + providerSpec := new(machinev1beta1.GCPMachineProviderSpec) |
| 91 | + err := unmarshalProviderSpecCPMS(&cpms, providerSpec) |
| 92 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 93 | + |
| 94 | + // Modify the boot image to a older known value. |
| 95 | + // See: https://issues.redhat.com/browse/OCPBUGS-57426 |
| 96 | + originalBootImage := providerSpec.Disks[0].Image |
| 97 | + newBootImage := "projects/rhcos-cloud/global/images/rhcos-410-84-202210040010-0-gcp-x86-64" |
| 98 | + jsonPatch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/disks/0/image", "value": "%s"}]`, newBootImage) |
| 99 | + |
| 100 | + // Create JSON patch to restore original disk image |
| 101 | + originalJSONPatch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/disks/0/image", "value": "%s"}]`, originalBootImage) |
| 102 | + |
| 103 | + return jsonPatch, originalJSONPatch, newBootImage, originalBootImage |
| 104 | +} |
| 105 | + |
| 106 | +// generateAzureProviderSpecPatchCPMS generates a fake update patch for the Azure ControlPlaneMachineSet |
| 107 | +func generateAzureProviderSpecPatchCPMS(cpms machinev1.ControlPlaneMachineSet) (string, string, string, string) { |
| 108 | + providerSpec := new(machinev1beta1.AzureMachineProviderSpec) |
| 109 | + err := unmarshalProviderSpecCPMS(&cpms, providerSpec) |
| 110 | + o.Expect(err).NotTo(o.HaveOccurred()) |
| 111 | + |
| 112 | + // Use JSON patch to precisely replace just the image field with marketplace image |
| 113 | + // This avoids any merge conflicts with existing fields |
| 114 | + // Use an older known 4.18 boot image that is available in the marketplace |
| 115 | + jsonPatch := `[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/image", "value": {"offer": "aro4", "publisher": "azureopenshift", "resourceID": "", "sku": "418-v2", "version": "418.94.20250122", "type": "MarketplaceNoPlan"}}]` |
| 116 | + |
| 117 | + // Create JSON patch to restore original image |
| 118 | + originalImage := providerSpec.Image |
| 119 | + originalJSONPatch := fmt.Sprintf(`[{"op": "replace", "path": "/spec/template/machines_v1beta1_machine_openshift_io/spec/providerSpec/value/image", "value": {"offer": "%s", "publisher": "%s", "resourceID": "%s", "sku": "%s", "version": "%s", "type": "%s"}}]`, |
| 120 | + originalImage.Offer, originalImage.Publisher, originalImage.ResourceID, originalImage.SKU, originalImage.Version, originalImage.Type) |
| 121 | + |
| 122 | + return jsonPatch, originalJSONPatch, "418.94.20250122", providerSpec.Image.Version |
| 123 | +} |
| 124 | + |
| 125 | +// unmarshalProviderSpecCPMS unmarshals the controlplanemachineset's provider spec into |
| 126 | +// a ProviderSpec object. Returns an error if providerSpec field is nil, |
| 127 | +// or the unmarshal fails |
| 128 | +func unmarshalProviderSpecCPMS(cpms *machinev1.ControlPlaneMachineSet, providerSpec interface{}) error { |
| 129 | + if cpms.Spec.Template.OpenShiftMachineV1Beta1Machine.Spec.ProviderSpec.Value == nil { |
| 130 | + return fmt.Errorf("providerSpec field was empty") |
| 131 | + } |
| 132 | + if err := yaml.Unmarshal(cpms.Spec.Template.OpenShiftMachineV1Beta1Machine.Spec.ProviderSpec.Value.Raw, &providerSpec); err != nil { |
| 133 | + return fmt.Errorf("unmarshal into providerSpec failed %w", err) |
| 134 | + } |
| 135 | + return nil |
| 136 | +} |
0 commit comments