Skip to content
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

Fix resource group not getting updated if tags are added #1721

Merged
merged 1 commit into from
Oct 26, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions api/v1beta1/tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ const (
// See https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
// for annotation formatting rules.
VMTagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-azure-last-applied-tags-vm"

// 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/
// for annotation formatting rules.
RGTagsLastAppliedAnnotation = "sigs.k8s.io/cluster-api-provider-azure-last-applied-tags-rg"
)

// SpecVersionHashTagKey is the key for the spec version hash used to enable quick spec difference comparison.
Expand Down
5 changes: 5 additions & 0 deletions azure/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ func WithIndex(name string, n int) string {
return fmt.Sprintf("%s-%d", name, n)
}

// ResourceGroupID returns the azure resource ID for a given resource group.
func ResourceGroupID(subscriptionID, resourceGroup string) string {
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s", subscriptionID, resourceGroup)
}

// VMID returns the azure resource ID for a given VM.
func VMID(subscriptionID, resourceGroup, vmName string) string {
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", subscriptionID, resourceGroup, vmName)
Expand Down
47 changes: 47 additions & 0 deletions azure/scope/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package scope

import (
"context"
"encoding/json"
"fmt"
"hash/fnv"
"strconv"
Expand Down Expand Up @@ -796,3 +797,49 @@ func (s *ClusterScope) UpdatePatchStatus(condition clusterv1.ConditionType, serv
conditions.MarkFalse(s.AzureCluster, condition, infrav1.FailedReason, clusterv1.ConditionSeverityError, "%s failed to update. err: %s", service, err.Error())
}
}

// AnnotationJSON returns a map[string]interface from a JSON annotation.
func (s *ClusterScope) AnnotationJSON(annotation string) (map[string]interface{}, error) {
out := map[string]interface{}{}
jsonAnnotation := s.AzureCluster.GetAnnotations()[annotation]
if len(jsonAnnotation) == 0 {
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 (s *ClusterScope) UpdateAnnotationJSON(annotation string, content map[string]interface{}) error {
b, err := json.Marshal(content)
if err != nil {
return err
}
s.SetAnnotation(annotation, string(b))
return nil
}

// SetAnnotation sets a key value annotation on the AzureCluster.
func (s *ClusterScope) SetAnnotation(key, value string) {
if s.AzureCluster.Annotations == nil {
s.AzureCluster.Annotations = map[string]string{}
}
s.AzureCluster.Annotations[key] = value
}

// TagsSpecs returns the tag specs for the AzureCluster.
func (s *ClusterScope) TagsSpecs() []azure.TagsSpec {
return []azure.TagsSpec{
{
Scope: azure.ResourceGroupID(s.SubscriptionID(), s.ResourceGroup()),
Tags: s.AdditionalTags(),
Annotation: infrav1.RGTagsLastAppliedAnnotation,
},
}
}
47 changes: 47 additions & 0 deletions azure/scope/managedcontrolplane.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package scope
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net"
"strings"
Expand Down Expand Up @@ -622,3 +623,49 @@ func (s *ManagedControlPlaneScope) UpdatePutStatus(condition clusterv1.Condition
func (s *ManagedControlPlaneScope) UpdatePatchStatus(condition clusterv1.ConditionType, service string, err error) {
// TODO: add condition to AzureManagedControlPlane status
}

// AnnotationJSON returns a map[string]interface from a JSON annotation.
func (s *ManagedControlPlaneScope) AnnotationJSON(annotation string) (map[string]interface{}, error) {
out := map[string]interface{}{}
jsonAnnotation := s.ControlPlane.GetAnnotations()[annotation]
if len(jsonAnnotation) == 0 {
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 (s *ManagedControlPlaneScope) UpdateAnnotationJSON(annotation string, content map[string]interface{}) error {
b, err := json.Marshal(content)
if err != nil {
return err
}
s.SetAnnotation(annotation, string(b))
return nil
}

// SetAnnotation sets a key value annotation on the ControlPlane.
func (s *ManagedControlPlaneScope) SetAnnotation(key, value string) {
if s.ControlPlane.Annotations == nil {
s.ControlPlane.Annotations = map[string]string{}
}
s.ControlPlane.Annotations[key] = value
}

// TagsSpecs returns the tag specs for the ManagedControlPlane.
func (s *ManagedControlPlaneScope) TagsSpecs() []azure.TagsSpec {
return []azure.TagsSpec{
{
Scope: azure.ResourceGroupID(s.SubscriptionID(), s.ResourceGroup()),
Tags: s.AdditionalTags(),
Annotation: infrav1.RGTagsLastAppliedAnnotation,
},
}
}
4 changes: 3 additions & 1 deletion azure/services/groups/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ func (s *GroupSpec) OwnerResourceName() string {
func (s *GroupSpec) Parameters(existing interface{}) (interface{}, error) {
if existing != nil {
// rg already exists, nothing to update.
// Note that rg tags are updated separately using tags service.
return nil, nil
}
return resources.Group{
Location: to.StringPtr(s.Location),
// We create only CAPZ default tags. User defined additional tags
// are created and updated using tags service.
Tags: converters.TagsToMap(infrav1.Build(infrav1.BuildParams{
ClusterName: s.ClusterName,
Lifecycle: infrav1.ResourceLifecycleOwned,
Name: to.StringPtr(s.Name),
Role: to.StringPtr(infrav1.CommonRole),
Additional: s.AdditionalTags,
karuppiah7890 marked this conversation as resolved.
Show resolved Hide resolved
})),
}, nil
}
11 changes: 6 additions & 5 deletions azure/services/tags/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
// client wraps go-sdk.
type client interface {
GetAtScope(context.Context, string) (resources.TagsResource, error)
CreateOrUpdateAtScope(context.Context, string, resources.TagsResource) (resources.TagsResource, error)
UpdateAtScope(context.Context, string, resources.TagsPatchResource) (resources.TagsResource, error)
}

// azureClient contains the Azure go-sdk Client.
Expand Down Expand Up @@ -60,10 +60,11 @@ func (ac *azureClient) GetAtScope(ctx context.Context, scope string) (resources.
return ac.tags.GetAtScope(ctx, scope)
}

// CreateOrUpdateAtScope allows adding or replacing the entire set of tags on the specified resource or subscription.
func (ac *azureClient) CreateOrUpdateAtScope(ctx context.Context, scope string, parameters resources.TagsResource) (resources.TagsResource, error) {
ctx, _, done := tele.StartSpanWithLogger(ctx, "tags.AzureClient.CreateOrUpdateAtScope")
// UpdateAtScope this operation allows replacing, merging or selectively deleting tags on the specified resource or
// subscription.
func (ac *azureClient) UpdateAtScope(ctx context.Context, scope string, parameters resources.TagsPatchResource) (resources.TagsResource, error) {
ctx, _, done := tele.StartSpanWithLogger(ctx, "tags.AzureClient.UpdateAtScope")
defer done()

return ac.tags.CreateOrUpdateAtScope(ctx, scope, parameters)
return ac.tags.UpdateAtScope(ctx, scope, parameters)
}
24 changes: 12 additions & 12 deletions azure/services/tags/mock_tags/client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 0 additions & 85 deletions azure/services/tags/mock_tags/tags_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading