From 3479f2337a2b90968d0e1b3ec9362bd753c2d049 Mon Sep 17 00:00:00 2001 From: Jon Huhn Date: Wed, 24 Jul 2024 17:59:59 +0000 Subject: [PATCH] don't rollout VMSS model updates when tags change --- azure/const.go | 6 +++ azure/defaults.go | 5 +++ azure/scope/machinepool.go | 39 +++++++++++++++++++ azure/types.go | 1 - azure/types_test.go | 12 ------ .../azuremachinepool_reconciler.go | 6 +++ 6 files changed, 56 insertions(+), 13 deletions(-) diff --git a/azure/const.go b/azure/const.go index cae610d71ec..67319251a99 100644 --- a/azure/const.go +++ b/azure/const.go @@ -23,6 +23,12 @@ const ( // for annotation formatting rules. VMTagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-azure-last-applied-tags-vm" + // VMSSTagsLastAppliedAnnotation is the key for the machine object annotation + // which tracks the AdditionalTags in the MachinePool Provider Config. + // See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + // for annotation formatting rules. + VMSSTagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-azure-last-applied-tags-vmss" + // RGTagsLastAppliedAnnotation is the key for the Azure Cluster object annotation // which tracks the AdditionalTags for Resource Group which is part in the Azure Cluster. // See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ diff --git a/azure/defaults.go b/azure/defaults.go index 9dcf227b149..1de9f60b574 100644 --- a/azure/defaults.go +++ b/azure/defaults.go @@ -217,6 +217,11 @@ func VMID(subscriptionID, resourceGroup, vmName string) string { return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, vmName) } +// VMSSID returns the azure resource ID for a given VMSS. +func VMSSID(subscriptionID, resourceGroup, vmssName string) string { + return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s", subscriptionID, resourceGroup, vmssName) +} + // VNetID returns the azure resource ID for a given VNet. func VNetID(subscriptionID, resourceGroup, vnetName string) string { return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s", subscriptionID, resourceGroup, vnetName) diff --git a/azure/scope/machinepool.go b/azure/scope/machinepool.go index eef85381c5d..e74bcafe7e8 100644 --- a/azure/scope/machinepool.go +++ b/azure/scope/machinepool.go @@ -20,6 +20,7 @@ import ( "context" "crypto/sha256" "encoding/base64" + "encoding/json" "fmt" "io" "strings" @@ -946,3 +947,41 @@ func (m *MachinePoolScope) ReconcileReplicas(ctx context.Context, vmss *azure.VM return nil } + +// AnnotationJSON returns a map[string]interface from a JSON annotation. +func (m *MachinePoolScope) AnnotationJSON(annotation string) (map[string]interface{}, error) { + out := map[string]interface{}{} + jsonAnnotation := m.AzureMachinePool.GetAnnotations()[annotation] + if jsonAnnotation == "" { + return out, nil + } + err := json.Unmarshal([]byte(jsonAnnotation), &out) + if err != nil { + return out, err + } + return out, nil +} + +// UpdateAnnotationJSON updates the `annotation` with +// `content`. `content` in this case should be a `map[string]interface{}` +// suitable for turning into JSON. This `content` map will be marshalled into a +// JSON string before being set as the given `annotation`. +func (m *MachinePoolScope) UpdateAnnotationJSON(annotation string, content map[string]interface{}) error { + b, err := json.Marshal(content) + if err != nil { + return err + } + m.SetAnnotation(annotation, string(b)) + return nil +} + +// TagsSpecs returns the tags for the AzureMachinePool. +func (m *MachinePoolScope) TagsSpecs() []azure.TagsSpec { + return []azure.TagsSpec{ + { + Scope: azure.VMSSID(m.SubscriptionID(), m.NodeResourceGroup(), m.Name()), + Tags: m.AdditionalTags(), + Annotation: azure.VMSSTagsLastAppliedAnnotation, + }, + } +} diff --git a/azure/types.go b/azure/types.go index 4583b57981c..2e040edcc2b 100644 --- a/azure/types.go +++ b/azure/types.go @@ -126,7 +126,6 @@ func (vmss VMSS) HasModelChanges(other VMSS) bool { equal := cmp.Equal(vmss.Image, other.Image) && cmp.Equal(vmss.Identity, other.Identity) && cmp.Equal(vmss.Zones, other.Zones) && - cmp.Equal(vmss.Tags, other.Tags) && cmp.Equal(vmss.Sku, other.Sku) return !equal } diff --git a/azure/types_test.go b/azure/types_test.go index a6b7194c1c7..f84b79761b6 100644 --- a/azure/types_test.go +++ b/azure/types_test.go @@ -124,18 +124,6 @@ func TestVMSS_HasModelChanges(t *testing.T) { }, HasModelChanges: true, }, - { - Name: "with different Tags", - Factory: func() (VMSS, VMSS) { - l := getDefaultVMSSForModelTesting() - l.Tags = infrav1.Tags{ - "bin": "baz", - } - r := getDefaultVMSSForModelTesting() - return r, l - }, - HasModelChanges: true, - }, } for _, c := range cases { diff --git a/exp/controllers/azuremachinepool_reconciler.go b/exp/controllers/azuremachinepool_reconciler.go index fe5efcadcf2..a51c1123725 100644 --- a/exp/controllers/azuremachinepool_reconciler.go +++ b/exp/controllers/azuremachinepool_reconciler.go @@ -25,6 +25,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" "sigs.k8s.io/cluster-api-provider-azure/azure/services/roleassignments" "sigs.k8s.io/cluster-api-provider-azure/azure/services/scalesets" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/tags" "sigs.k8s.io/cluster-api-provider-azure/util/tele" ) @@ -49,12 +50,17 @@ func newAzureMachinePoolService(machinePoolScope *scope.MachinePoolScope) (*azur if err != nil { return nil, errors.Wrap(err, "failed to create a scalesets service") } + tagsSvc, err := tags.New(machinePoolScope) + if err != nil { + return nil, errors.Wrap(err, "failed creating tags service") + } return &azureMachinePoolService{ scope: machinePoolScope, services: []azure.ServiceReconciler{ scaleSetsSvc, roleAssignmentsSvc, + tagsSvc, }, skuCache: cache, }, nil