Skip to content
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 44 additions & 127 deletions pkg/controllers/machinesetsync/machineset_sync_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"slices"
"sort"

"github.com/go-logr/logr"
configv1 "github.com/openshift/api/config/v1"
Expand All @@ -40,7 +39,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/go-test/deep"
machinev1applyconfigs "github.com/openshift/client-go/machine/applyconfigurations/machine/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
Expand Down Expand Up @@ -775,7 +773,7 @@ func (r *MachineSetSyncReconciler) createOrUpdateCAPIInfraMachineTemplate(ctx co
return updateErr
}

if len(capiInfraMachineTemplatesDiff) == 0 {
if !capiInfraMachineTemplatesDiff.HasChanges() {
logger.Info("No changes detected for CAPI infra machine template")
return nil
}
Expand Down Expand Up @@ -820,7 +818,10 @@ func (r *MachineSetSyncReconciler) createOrUpdateCAPIMachineSet(ctx context.Cont
// If there is an existing CAPI machine set, work out if it needs updating.

// Compare the existing CAPI machine set with the converted CAPI machine set to check for changes.
capiMachineSetsDiff := compareCAPIMachineSets(existingCAPIMachineSet, convertedCAPIMachineSet)
capiMachineSetsDiff, err := compareCAPIMachineSets(existingCAPIMachineSet, convertedCAPIMachineSet)
if err != nil {
return fmt.Errorf("failed to compare Cluster API machine sets: %w", err)
}

// Make a deep copy of the converted CAPI machine set to avoid modifying the original.
updatedOrCreatedCAPIMachineSet := convertedCAPIMachineSet.DeepCopy()
Expand Down Expand Up @@ -873,11 +874,11 @@ func (r *MachineSetSyncReconciler) ensureCAPIMachineSet(ctx context.Context, sou
}

// ensureCAPIMachineSetSpecUpdated updates the CAPI machine set if changes are detected.
func (r *MachineSetSyncReconciler) ensureCAPIMachineSetSpecUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, capiMachineSetsDiff map[string]any, updatedOrCreatedCAPIMachineSet *clusterv1.MachineSet) (bool, error) {
func (r *MachineSetSyncReconciler) ensureCAPIMachineSetSpecUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, capiMachineSetsDiff util.DiffResult, updatedOrCreatedCAPIMachineSet *clusterv1.MachineSet) (bool, error) {
logger := logf.FromContext(ctx)

// If there are no spec changes, return early.
if !hasSpecOrMetadataOrProviderSpecChanges(capiMachineSetsDiff) {
if !(capiMachineSetsDiff.HasMetadataChanges() || capiMachineSetsDiff.HasSpecChanges() || capiMachineSetsDiff.HasProviderSpecChanges()) {
return false, nil
}

Expand All @@ -899,11 +900,11 @@ func (r *MachineSetSyncReconciler) ensureCAPIMachineSetSpecUpdated(ctx context.C
}

// ensureCAPIMachineSetStatusUpdated updates the CAPI machine set status if changes are detected and conditions are met.
func (r *MachineSetSyncReconciler) ensureCAPIMachineSetStatusUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, existingCAPIMachineSet *clusterv1.MachineSet, convertedCAPIMachineSet *clusterv1.MachineSet, updatedOrCreatedCAPIMachineSet *clusterv1.MachineSet, capiMachineSetsDiff map[string]any, specUpdated bool) (bool, error) {
func (r *MachineSetSyncReconciler) ensureCAPIMachineSetStatusUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, existingCAPIMachineSet *clusterv1.MachineSet, convertedCAPIMachineSet *clusterv1.MachineSet, updatedOrCreatedCAPIMachineSet *clusterv1.MachineSet, capiMachineSetsDiff util.DiffResult, specUpdated bool) (bool, error) {
logger := logf.FromContext(ctx)

// If there are no status changes and the spec has not been updated, return early.
if !hasStatusChanges(capiMachineSetsDiff) && !specUpdated {
if !capiMachineSetsDiff.HasStatusChanges() && !specUpdated {
return false, nil
}

Expand Down Expand Up @@ -950,11 +951,11 @@ func (r *MachineSetSyncReconciler) ensureCAPIMachineSetStatusUpdated(ctx context
}

// ensureMAPIMachineSetSpecUpdated updates the MAPI machine set if changes are detected.
func (r *MachineSetSyncReconciler) ensureMAPIMachineSetSpecUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, mapiMachineSetsDiff map[string]any, updatedMAPIMachineSet *mapiv1beta1.MachineSet) (bool, error) {
func (r *MachineSetSyncReconciler) ensureMAPIMachineSetSpecUpdated(ctx context.Context, mapiMachineSet *mapiv1beta1.MachineSet, mapiMachineSetsDiff util.DiffResult, updatedMAPIMachineSet *mapiv1beta1.MachineSet) (bool, error) {
logger := logf.FromContext(ctx)

// If there are no spec changes, return early.
if !hasSpecOrMetadataOrProviderSpecChanges(mapiMachineSetsDiff) {
if !(mapiMachineSetsDiff.HasMetadataChanges() || mapiMachineSetsDiff.HasSpecChanges() || mapiMachineSetsDiff.HasProviderSpecChanges()) {
return false, nil
}

Expand All @@ -977,11 +978,11 @@ func (r *MachineSetSyncReconciler) ensureMAPIMachineSetSpecUpdated(ctx context.C
}

// ensureMAPIMachineSetStatusUpdated updates the MAPI machine set status if changes are detected and conditions are met.
func (r *MachineSetSyncReconciler) ensureMAPIMachineSetStatusUpdated(ctx context.Context, existingMAPIMachineSet *mapiv1beta1.MachineSet, convertedMAPIMachineSet *mapiv1beta1.MachineSet, updatedMAPIMachineSet *mapiv1beta1.MachineSet, sourceCAPIMachineSet *clusterv1.MachineSet, mapiMachineSetsDiff map[string]any, specUpdated bool) (bool, error) {
func (r *MachineSetSyncReconciler) ensureMAPIMachineSetStatusUpdated(ctx context.Context, existingMAPIMachineSet *mapiv1beta1.MachineSet, convertedMAPIMachineSet *mapiv1beta1.MachineSet, updatedMAPIMachineSet *mapiv1beta1.MachineSet, sourceCAPIMachineSet *clusterv1.MachineSet, mapiMachineSetsDiff util.DiffResult, specUpdated bool) (bool, error) {
logger := logf.FromContext(ctx)

// If there are no status changes and the spec has not been updated, return early.
if !hasStatusChanges(mapiMachineSetsDiff) && !specUpdated {
if !mapiMachineSetsDiff.HasStatusChanges() && !specUpdated {
return false, nil
}

Expand Down Expand Up @@ -1051,7 +1052,7 @@ func (r *MachineSetSyncReconciler) updateMAPIMachineSet(ctx context.Context, exi

// Here we always assume the existingMAPIMachineSet already exists, so we don't need to create it.

mapiMachineSetsDiff, err := compareMAPIMachineSets(existingMAPIMachineSet, convertedMAPIMachineSet)
mapiMachineSetsDiff, err := compareMAPIMachineSets(r.Platform, existingMAPIMachineSet, convertedMAPIMachineSet)
if err != nil {
return fmt.Errorf("unable to compare MAPI machine sets: %w", err)
}
Expand Down Expand Up @@ -1347,9 +1348,9 @@ func initInfraMachineTemplateListAndInfraClusterListFromProvider(platform config
}

// compareCAPIInfraMachineTemplates compares CAPI infra machine templates a and b, and returns a list of differences, or none if there are none.
//
//nolint:funlen
func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachineTemplate1, infraMachineTemplate2 client.Object) (map[string]any, error) {
func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachineTemplate1, infraMachineTemplate2 client.Object) (util.DiffResult, error) {
var obj1, obj2 client.Object

switch platform {
case configv1.AWSPlatformType:
typedInfraMachineTemplate1, ok := infraMachineTemplate1.(*awsv1.AWSMachineTemplate)
Expand All @@ -1362,19 +1363,8 @@ func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachi
return nil, errAssertingCAPIAWSMachineTemplate
}

diff := make(map[string]any)

if diffSpec := deep.Equal(typedInfraMachineTemplate1.Spec, typedinfraMachineTemplate2.Spec); len(diffSpec) > 0 {
diff[".spec"] = diffSpec
}

if diffObjectMeta := util.ObjectMetaEqual(typedInfraMachineTemplate1.ObjectMeta, typedinfraMachineTemplate2.ObjectMeta); len(diffObjectMeta) > 0 {
diff[".metadata"] = diffObjectMeta
}

// TODO: Evaluate if we want to add status comparison if needed in the future (e.g. for scale from zero capacity).

return diff, nil
obj1 = typedInfraMachineTemplate1
obj2 = typedinfraMachineTemplate2
case configv1.OpenStackPlatformType:
typedInfraMachineTemplate1, ok := infraMachineTemplate1.(*openstackv1.OpenStackMachineTemplate)
if !ok {
Expand All @@ -1386,17 +1376,8 @@ func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachi
return nil, errAssertingCAPIOpenStackMachineTemplate
}

diff := make(map[string]any)

if diffSpec := deep.Equal(typedInfraMachineTemplate1.Spec, typedinfraMachineTemplate2.Spec); len(diffSpec) > 0 {
diff[".spec"] = diffSpec
}

if diffObjectMeta := deep.Equal(typedInfraMachineTemplate1.ObjectMeta, typedinfraMachineTemplate2.ObjectMeta); len(diffObjectMeta) > 0 {
diff[".metadata"] = diffObjectMeta
}

return diff, nil
obj1 = typedInfraMachineTemplate1
obj2 = typedinfraMachineTemplate2
case configv1.PowerVSPlatformType:
typedInfraMachineTemplate1, ok := infraMachineTemplate1.(*ibmpowervsv1.IBMPowerVSMachineTemplate)
if !ok {
Expand All @@ -1408,92 +1389,44 @@ func compareCAPIInfraMachineTemplates(platform configv1.PlatformType, infraMachi
return nil, errAssertingCAPIIBMPowerVSMachineTemplate
}

diff := make(map[string]any)

if diffSpec := deep.Equal(typedInfraMachineTemplate1.Spec, typedinfraMachineTemplate2.Spec); len(diffSpec) > 0 {
diff[".spec"] = diffSpec
}

if diffObjectMeta := deep.Equal(typedInfraMachineTemplate1.ObjectMeta, typedinfraMachineTemplate2.ObjectMeta); len(diffObjectMeta) > 0 {
diff[".metadata"] = diffObjectMeta
}

// TODO: Evaluate if we want to add status comparison if needed in the future (e.g. for scale from zero capacity).

return diff, nil
obj1 = typedInfraMachineTemplate1
obj2 = typedinfraMachineTemplate2
default:
return nil, fmt.Errorf("%w: %s", errPlatformNotSupported, platform)
}
}

// compareCAPIMachineSets compares CAPI machineSets a and b, and returns a list of differences, or none if there are none.
func compareCAPIMachineSets(capiMachineSet1, capiMachineSet2 *clusterv1.MachineSet) map[string]any {
diff := make(map[string]any)

if diffSpec := deep.Equal(capiMachineSet1.Spec, capiMachineSet2.Spec); len(diffSpec) > 0 {
diff[".spec"] = diffSpec
diff, err := util.NewDefaultDiffer().Diff(obj1, obj2)
if err != nil {
return nil, fmt.Errorf("failed to compare Cluster API infrastructure machine templates: %w", err)
}

if diffObjectMeta := util.ObjectMetaEqual(capiMachineSet1.ObjectMeta, capiMachineSet2.ObjectMeta); len(diffObjectMeta) > 0 {
diff[".metadata"] = diffObjectMeta
}
return diff, nil
}

if diffStatus := util.CAPIMachineSetStatusEqual(capiMachineSet1.Status, capiMachineSet2.Status); len(diffStatus) > 0 {
diff[".status"] = diffStatus
// compareCAPIMachineSets compares Cluster API machineSets a and b, and returns a list of differences, or none if there are none.
func compareCAPIMachineSets(capiMachineSet1, capiMachineSet2 *clusterv1.MachineSet) (util.DiffResult, error) {
diff, err := util.NewDefaultDiffer().Diff(capiMachineSet1, capiMachineSet2)
if err != nil {
return nil, fmt.Errorf("failed to compare Cluster API machinesets: %w", err)
}

return diff
return diff, nil
}

// compareMAPIMachineSets compares MAPI machineSets a and b, and returns a list of differences, or none if there are none.
func compareMAPIMachineSets(a, b *mapiv1beta1.MachineSet) (map[string]any, error) {
diff := make(map[string]any)
func compareMAPIMachineSets(platform configv1.PlatformType, a, b *mapiv1beta1.MachineSet) (util.DiffResult, error) {
diff, err := util.NewDefaultDiffer(
util.WithProviderSpec(platform, []string{"spec", "template", "spec", "providerSpec", "value"}, mapi2capi.ProviderSpecFromRawExtension),

ps1, err := mapi2capi.AWSProviderSpecFromRawExtension(a.Spec.Template.Spec.ProviderSpec.Value)
if err != nil {
return nil, fmt.Errorf("unable to parse first MAPI machine set providerSpec: %w", err)
}
// Other status fields to ignore
util.WithIgnoreField("status", "replicas"),
util.WithIgnoreField("status", "observedGeneration"),
util.WithIgnoreField("status", "authoritativeAPI"),
util.WithIgnoreField("status", "synchronizedGeneration"),
).Diff(a, b)

ps2, err := mapi2capi.AWSProviderSpecFromRawExtension(b.Spec.Template.Spec.ProviderSpec.Value)
if err != nil {
return nil, fmt.Errorf("unable to parse second MAPI machine set providerSpec: %w", err)
}

// Sort the tags by name to ensure consistent ordering.
// On the CAPI side these tags are in a map,
// so the order is not guaranteed when converting back from a CAPI map to a MAPI slice.
sort.Slice(ps1.Tags, func(i, j int) bool {
return ps1.Tags[i].Name < ps1.Tags[j].Name
})

// Sort the tags by name to ensure consistent ordering.
// On the CAPI side these tags are in a map,
// so the order is not guaranteed when converting back from a CAPI map to a MAPI slice.
sort.Slice(ps2.Tags, func(i, j int) bool {
return ps2.Tags[i].Name < ps2.Tags[j].Name
})

if diffProviderSpec := deep.Equal(ps1, ps2); len(diffProviderSpec) > 0 {
diff[".providerSpec"] = diffProviderSpec
}

// Remove the providerSpec from the Spec as we've already compared them.
aCopy := a.DeepCopy()
aCopy.Spec.Template.Spec.ProviderSpec.Value = nil

bCopy := b.DeepCopy()
bCopy.Spec.Template.Spec.ProviderSpec.Value = nil

if diffSpec := deep.Equal(aCopy.Spec, bCopy.Spec); len(diffSpec) > 0 {
diff[".spec"] = diffSpec
}

if diffMetadata := util.ObjectMetaEqual(aCopy.ObjectMeta, bCopy.ObjectMeta); len(diffMetadata) > 0 {
diff[".metadata"] = diffMetadata
}

if diffStatus := util.MAPIMachineSetStatusEqual(a.Status, b.Status); len(diffStatus) > 0 {
diff[".status"] = diffStatus
return nil, fmt.Errorf("failed to compare Machine API machinesets: %w", err)
}

return diff, nil
Expand Down Expand Up @@ -1551,22 +1484,6 @@ func restoreMAPIFields(existingMAPIMachineSet, convertedMAPIMachineSet *mapiv1be
convertedMAPIMachineSet.SetFinalizers(existingMAPIMachineSet.GetFinalizers())
}

// hasStatusChanges returns true if there are changes to the status.
func hasStatusChanges(diff map[string]any) bool {
_, ok := diff[".status"]

return ok
}

// hasSpecOrMetadataOrProviderSpecChanges returns true if there are changes to the spec, metadata, or providerSpec.
func hasSpecOrMetadataOrProviderSpecChanges(diff map[string]any) bool {
_, ok1 := diff[".spec"]
_, ok2 := diff[".metadata"]
_, ok3 := diff[".providerSpec"]

return ok1 || ok2 || ok3
}

// isTerminalConfigurationError returns true if the provided error is
// errInvalidClusterInfraReference or errInvalidInfraMachineTemplateReference.
func isTerminalConfigurationError(err error) bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1190,18 +1190,17 @@ var _ = Describe("compareMAPIMachineSets", func() {

Context("when comparing MachineSets with different instance types", func() {
It("should detect differences in providerSpec", func() {
diff, err := compareMAPIMachineSets(mapiMachineSet1, mapiMachineSet2)
diff, err := compareMAPIMachineSets(configv1.AWSPlatformType, mapiMachineSet1, mapiMachineSet2)
Expect(err).NotTo(HaveOccurred())
Expect(diff).To(HaveKey(".providerSpec"))
Expect(diff[".providerSpec"]).NotTo(BeEmpty())
Expect(diff.HasProviderSpecChanges()).To(BeTrue())
})
})

Context("when comparing identical MachineSets", func() {
It("should detect no differences", func() {
diff, err := compareMAPIMachineSets(mapiMachineSet1, mapiMachineSet1)
diff, err := compareMAPIMachineSets(configv1.AWSPlatformType, mapiMachineSet1, mapiMachineSet1)
Expect(err).NotTo(HaveOccurred())
Expect(diff).To(BeEmpty())
Expect(diff.HasChanges()).To(BeFalse())
})
})
})
Expand Down
Loading