Repository: aws-controllers-k8s/acmpca-controller
Pull request #83: Prepare repository for tag conversion function signature update
Author: login: michaelhtm
Labels: name: lgtm
description: Indicates that a PR is ready to be merged.
name: approved
Created: 2025-03-13T04:09:26Z | Merged: 2025-03-28T17:56:09Z

Description of changes:
The To/FromACKTags function signatures are changing with 
the next code-gen release. This PR aims to ensure custom 
code adapts as well.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.



Diff:
diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml
index 9bd1752..6df05e6 100755
--- a/apis/v1alpha1/ack-generate-metadata.yaml
+++ b/apis/v1alpha1/ack-generate-metadata.yaml
@@ -1,8 +1,8 @@
 ack_generate_info:
-  build_date: "2025-02-21T00:50:58Z"
-  build_hash: a326346bd3a6973254d247c9ab2dc76790c36241
+  build_date: "2025-03-14T20:14:28Z"
+  build_hash: 0ab258c51775fd52af32e649da00d57373b20869
   go_version: go1.24.0
-  version: v0.43.2
+  version: v0.43.2-6-g0ab258c
 api_directory_checksum: b40da118f1911726e790de72e761cf61b62f1a5e
 api_version: v1alpha1
 aws_sdk_go_version: 1.32.6
diff --git a/pkg/resource/certificate_authority/delta.go b/pkg/resource/certificate_authority/delta.go
index 7d100af..8aca31e 100644
--- a/pkg/resource/certificate_authority/delta.go
+++ b/pkg/resource/certificate_authority/delta.go
@@ -317,7 +317,9 @@ func newResourceDelta(
 			}
 		}
 	}
-	if !ackcompare.MapStringStringEqual(ToACKTags(a.ko.Spec.Tags), ToACKTags(b.ko.Spec.Tags)) {
+	desiredACKTags, _ := convertToOrderedACKTags(a.ko.Spec.Tags)
+	latestACKTags, _ := convertToOrderedACKTags(b.ko.Spec.Tags)
+	if !ackcompare.MapStringStringEqual(desiredACKTags, latestACKTags) {
 		delta.Add("Spec.Tags", a.ko.Spec.Tags, b.ko.Spec.Tags)
 	}
 	if ackcompare.HasNilDifference(a.ko.Spec.Type, b.ko.Spec.Type) {
diff --git a/pkg/resource/certificate_authority/hooks.go b/pkg/resource/certificate_authority/hooks.go
index 4675033..4d8437e 100644
--- a/pkg/resource/certificate_authority/hooks.go
+++ b/pkg/resource/certificate_authority/hooks.go
@@ -69,13 +69,13 @@ func (rm *resourceManager) syncTags(
 
 	arn := (*string)(latest.ko.Status.ACKResourceMetadata.ARN)
 
-	desiredTags := ToACKTags(desired.ko.Spec.Tags)
-	latestTags := ToACKTags(latest.ko.Spec.Tags)
+	desiredTags, _ := convertToOrderedACKTags(desired.ko.Spec.Tags)
+	latestTags, _ := convertToOrderedACKTags(latest.ko.Spec.Tags)
 
 	added, _, removed := ackcompare.GetTagsDifference(latestTags, desiredTags)
 
-	toAdd := FromACKTags(added)
-	toRemove := FromACKTags(removed)
+	toAdd := fromACKTags(added, nil)
+	toRemove := fromACKTags(removed, nil)
 
 	/*var toDelete []*string
 	for _, removedElement := range toRemove {
diff --git a/pkg/resource/certificate_authority/manager.go b/pkg/resource/certificate_authority/manager.go
index 8c74979..974eb53 100644
--- a/pkg/resource/certificate_authority/manager.go
+++ b/pkg/resource/certificate_authority/manager.go
@@ -291,9 +291,9 @@ func (rm *resourceManager) EnsureTags(
 	defaultTags := ackrt.GetDefaultTags(&rm.cfg, r.ko, md)
 	var existingTags []*svcapitypes.Tag
 	existingTags = r.ko.Spec.Tags
-	resourceTags := ToACKTags(existingTags)
+	resourceTags, keyOrder := convertToOrderedACKTags(existingTags)
 	tags := acktags.Merge(resourceTags, defaultTags)
-	r.ko.Spec.Tags = FromACKTags(tags)
+	r.ko.Spec.Tags = fromACKTags(tags, keyOrder)
 	return nil
 }
 
@@ -310,9 +310,9 @@ func (rm *resourceManager) FilterSystemTags(res acktypes.AWSResource) {
 	}
 	var existingTags []*svcapitypes.Tag
 	existingTags = r.ko.Spec.Tags
-	resourceTags := ToACKTags(existingTags)
+	resourceTags, tagKeyOrder := convertToOrderedACKTags(existingTags)
 	ignoreSystemTags(resourceTags)
-	r.ko.Spec.Tags = FromACKTags(resourceTags)
+	r.ko.Spec.Tags = fromACKTags(resourceTags, tagKeyOrder)
 }
 
 // mirrorAWSTags ensures that AWS tags are included in the desired resource
@@ -334,10 +334,10 @@ func mirrorAWSTags(a *resource, b *resource) {
 	var existingDesiredTags []*svcapitypes.Tag
 	existingDesiredTags = a.ko.Spec.Tags
 	existingLatestTags = b.ko.Spec.Tags
-	desiredTags := ToACKTags(existingDesiredTags)
-	latestTags := ToACKTags(existingLatestTags)
+	desiredTags, desiredTagKeyOrder := convertToOrderedACKTags(existingDesiredTags)
+	latestTags, _ := convertToOrderedACKTags(existingLatestTags)
 	syncAWSTags(desiredTags, latestTags)
-	a.ko.Spec.Tags = FromACKTags(desiredTags)
+	a.ko.Spec.Tags = fromACKTags(desiredTags, desiredTagKeyOrder)
 }
 
 // newResourceManager returns a new struct implementing
diff --git a/pkg/resource/certificate_authority/tags.go b/pkg/resource/certificate_authority/tags.go
index 5d748bc..fa93302 100644
--- a/pkg/resource/certificate_authority/tags.go
+++ b/pkg/resource/certificate_authority/tags.go
@@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
-	if tags == nil || len(tags) == 0 {
-		return result
-	}
+	keyOrder := []string{}
 
+	if len(tags) == 0 {
+		return result, keyOrder
+	}
 	for _, t := range tags {
 		if t.Key != nil {
-			if t.Value == nil {
-				result[*t.Key] = ""
-			} else {
+			keyOrder = append(keyOrder, *t.Key)
+			if t.Value != nil {
 				result[*t.Key] = *t.Value
+			} else {
+				result[*t.Key] = ""
 			}
 		}
 	}
 
-	return result
+	return result, keyOrder
 }
 
-// FromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
+// fromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
 // This method helps in setting the tags back inside AWSResource after merging
-// default controller tags with existing resource tags.
-func FromACKTags(tags acktags.Tags) []*svcapitypes.Tag {
+// default controller tags with existing resource tags. When a list,
+// it maintains the order from original
+func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag {
 	result := []*svcapitypes.Tag{}
+
+	for _, k := range keyOrder {
+		v, ok := tags[k]
+		if ok {
+			tag := svcapitypes.Tag{Key: &k, Value: &v}
+			result = append(result, &tag)
+			delete(tags, k)
+		}
+	}
 	for k, v := range tags {
-		kCopy := k
-		vCopy := v
-		tag := svcapitypes.Tag{Key: &kCopy, Value: &vCopy}
+		tag := svcapitypes.Tag{Key: &k, Value: &v}
 		result = append(result, &tag)
 	}
+
 	return result
 }
 


Reviews:
user: login: a-hilaly
body: Nice, ty @michaelhtm - please see comments inline
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T01:22:08Z
user: login: michaelhtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T01:26:34Z
user: login: michaelhtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T01:27:30Z

Review comments:
diff_hunk: @@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
+	keyOrder := []string{}
+
 	if tags == nil || len(tags) == 0 {
-		return result
+		return result, keyOrder
 	}
path: pkg/resource/certificate_authority/tags.go
user: login: a-hilaly
body: There is no need to check `tags == nil`. Checkout https://github.com/golang/go/blob/master/src/builtin/builtin.go#L163-L175. Quoting "Slice, or map: the number of elements in v; if v is nil, len(v) is zero."
created_at: 2025-03-14T01:19:01Z
author_association: MEMBER
diff_hunk: @@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
+	keyOrder := []string{}
+
 	if tags == nil || len(tags) == 0 {
-		return result
+		return result, keyOrder
 	}
-
 	for _, t := range tags {
 		if t.Key != nil {
-			if t.Value == nil {
-				result[*t.Key] = ""
+			keyOrder = append(keyOrder, *t.Key)
+			if t.Value != nil {
+				result[*t.Key] = *t.Value
 			} else {
 				result[*t.Key] = *t.Value
 			}
path: pkg/resource/certificate_authority/tags.go
user: login: a-hilaly
body: Did you mean:
```suggestion
			} else {
				result[*t.Key] = nil
			}
```
created_at: 2025-03-14T01:20:02Z
author_association: MEMBER
diff_hunk: @@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
+	keyOrder := []string{}
+
 	if tags == nil || len(tags) == 0 {
-		return result
+		return result, keyOrder
 	}
-
 	for _, t := range tags {
 		if t.Key != nil {
-			if t.Value == nil {
-				result[*t.Key] = ""
+			keyOrder = append(keyOrder, *t.Key)
+			if t.Value != nil {
+				result[*t.Key] = *t.Value
 			} else {
 				result[*t.Key] = *t.Value
 			}
 		}
 	}
 
-	return result
+	return result, keyOrder
 }
 
-// FromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
+// fromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
 // This method helps in setting the tags back inside AWSResource after merging
-// default controller tags with existing resource tags.
-func FromACKTags(tags acktags.Tags) []*svcapitypes.Tag {
+// default controller tags with existing resource tags. When a list,
+// it maintains the order from original
+func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag {
 	result := []*svcapitypes.Tag{}
+
+	for _, k := range keyOrder {
+		v, ok := tags[k]
+		if ok {
+			tag := svcapitypes.Tag{Key: &k, Value: &v}
+			result = append(result, &tag)
+			delete(tags, k)
path: pkg/resource/certificate_authority/tags.go
user: login: a-hilaly
body: Why do you need to delete `k` from the tags map?
created_at: 2025-03-14T01:21:09Z
author_association: MEMBER
diff_hunk: @@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
+	keyOrder := []string{}
+
 	if tags == nil || len(tags) == 0 {
-		return result
+		return result, keyOrder
 	}
-
 	for _, t := range tags {
 		if t.Key != nil {
-			if t.Value == nil {
-				result[*t.Key] = ""
+			keyOrder = append(keyOrder, *t.Key)
+			if t.Value != nil {
+				result[*t.Key] = *t.Value
 			} else {
 				result[*t.Key] = *t.Value
 			}
 		}
 	}
 
-	return result
+	return result, keyOrder
 }
 
-// FromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
+// fromACKTags converts the tags parameter into []*svcapitypes.Tag shape.
 // This method helps in setting the tags back inside AWSResource after merging
-// default controller tags with existing resource tags.
-func FromACKTags(tags acktags.Tags) []*svcapitypes.Tag {
+// default controller tags with existing resource tags. When a list,
+// it maintains the order from original
+func fromACKTags(tags acktags.Tags, keyOrder []string) []*svcapitypes.Tag {
 	result := []*svcapitypes.Tag{}
+
+	for _, k := range keyOrder {
+		v, ok := tags[k]
+		if ok {
+			tag := svcapitypes.Tag{Key: &k, Value: &v}
+			result = append(result, &tag)
+			delete(tags, k)
path: pkg/resource/certificate_authority/tags.go
user: login: michaelhtm
body: because after that i loop the map to retrieve any tags that would be added during the mirrorAWSTags, so i want to make sure I remove them to avoid duplicates
created_at: 2025-03-14T01:26:34Z
author_association: MEMBER
diff_hunk: @@ -30,39 +30,51 @@ var (
 	ACKSystemTags = []string{"services.k8s.aws/namespace", "services.k8s.aws/controller-version"}
 )
 
-// ToACKTags converts the tags parameter into 'acktags.Tags' shape.
+// convertToOrderedACKTags converts the tags parameter into 'acktags.Tags' shape.
 // This method helps in creating the hub(acktags.Tags) for merging
-// default controller tags with existing resource tags.
-func ToACKTags(tags []*svcapitypes.Tag) acktags.Tags {
+// default controller tags with existing resource tags. It also returns a slice
+// of keys maintaining the original key Order when the tags are a list
+func convertToOrderedACKTags(tags []*svcapitypes.Tag) (acktags.Tags, []string) {
 	result := acktags.NewTags()
+	keyOrder := []string{}
+
 	if tags == nil || len(tags) == 0 {
-		return result
+		return result, keyOrder
 	}
path: pkg/resource/certificate_authority/tags.go
user: login: michaelhtm
body: Agree, This mostly remained from ToACKTags..
created_at: 2025-03-14T01:27:30Z
author_association: MEMBER

PR comments:
user: login: ack-prow[bot]
created_at: 2025-03-14T20:53:38Z
body: @michaelhtm: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
acmpca-verify-attribution | 0f25b1702ee6d4920e5edae6c6f7871e2fb68259 | [link](https://prow.ack.aws.dev/view/s3/ack-prow-logs/pr-logs/pull/aws-controllers-k8s_acmpca-controller/83/acmpca-verify-attribution/1900651500401594368) | false | `/test acmpca-verify-attribution`

[Full PR test history](https://prow.ack.aws.dev/pr-history?org=aws-controllers-k8s&repo=acmpca-controller&pr=83). [Your PR dashboard](https://prow.ack.aws.dev/pr?query=is:pr+state:open+author:michaelhtm).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
author_association: NONE
user: login: rushmash91
created_at: 2025-03-28T17:55:36Z
body: /lgtm
author_association: CONTRIBUTOR
user: login: ack-prow[bot]
created_at: 2025-03-28T17:55:42Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/aws-controllers-k8s/acmpca-controller/pull/83#" title="Author self-approved">michaelhtm</a>*, *<a href="https://github.com/aws-controllers-k8s/acmpca-controller/pull/83#issuecomment-2762059503" title="LGTM">rushmash91</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=aws-controllers-k8s%2Facmpca-controller).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/aws-controllers-k8s/acmpca-controller/blob/main/OWNERS)~~ [michaelhtm,rushmash91]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE