diff --git a/.changelog/6793.txt b/.changelog/6793.txt new file mode 100644 index 0000000000..800912ef4f --- /dev/null +++ b/.changelog/6793.txt @@ -0,0 +1,3 @@ +```release-note:bug +apigee: Fixed permadiff on consumer_accept_list for `google_apigee_instance` +``` diff --git a/google/resource_apigee_instance.go b/google/resource_apigee_instance.go index eed5fc3ae0..2b52c31c8d 100644 --- a/google/resource_apigee_instance.go +++ b/google/resource_apigee_instance.go @@ -24,6 +24,31 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) +// Supress diffs when the lists of project have the same number of entries to handle the case that +// API does not return what the user originally provided. Instead, API does some transformation. +// For example, user provides a list of project number, but API returns a list of project Id. +func projectListDiffSuppress(_, _, _ string, d *schema.ResourceData) bool { + return projectListDiffSuppressFunc(d) +} + +func projectListDiffSuppressFunc(d TerraformResourceDataChange) bool { + kLength := "consumer_accept_list.#" + oldLength, newLength := d.GetChange(kLength) + + oldInt, ok := oldLength.(int) + if !ok { + return false + } + + newInt, ok := newLength.(int) + if !ok { + return false + } + log.Printf("[DEBUG] - suppressing diff with oldInt %d, newInt %d", oldInt, newInt) + + return oldInt == newInt +} + func resourceApigeeInstance() *schema.Resource { return &schema.Resource{ Create: resourceApigeeInstanceCreate, @@ -60,10 +85,11 @@ func resourceApigeeInstance() *schema.Resource { in the format 'organizations/{{org_name}}'.`, }, "consumer_accept_list": { - Type: schema.TypeList, - Computed: true, - Optional: true, - ForceNew: true, + Type: schema.TypeList, + Computed: true, + Optional: true, + ForceNew: true, + DiffSuppressFunc: projectListDiffSuppress, Description: `Optional. Customer accept list represents the list of projects (id/number) on customer side that can privately connect to the service attachment. It is an optional field which the customers can provide during the instance creation. By default, the customer diff --git a/google/resource_apigee_instance_test.go b/google/resource_apigee_instance_test.go new file mode 100644 index 0000000000..d13a9608fb --- /dev/null +++ b/google/resource_apigee_instance_test.go @@ -0,0 +1,111 @@ +package google + +import ( + "testing" +) + +func TestUnitApigeeInstance_projectListDiffSuppress(t *testing.T) { + for _, tc := range apigeeInstanceDiffSuppressTestCases { + tc.Test(t) + } +} + +type ApigeeInstanceDiffSuppressTestCase struct { + Name string + KeysToSuppress []string + Before map[string]interface{} + After map[string]interface{} +} + +var apigeeInstanceDiffSuppressTestCases = []ApigeeInstanceDiffSuppressTestCase{ + { + Name: "projects with the same length and one project entry is converted to project id", + KeysToSuppress: []string{"consumer_accept_list.0"}, + Before: map[string]interface{}{ + "consumer_accept_list.#": 2, + "consumer_accept_list.0": "45796856818", + "consumer_accept_list.1": "12345", + }, + After: map[string]interface{}{ + "consumer_accept_list.#": 2, + "consumer_accept_list.0": "tf-test8v1bd04pxa", + "consumer_accept_list.1": "12345", + }, + }, + { + Name: "projects with the same length and no project conversion", + KeysToSuppress: []string{}, + Before: map[string]interface{}{ + "consumer_accept_list.#": 2, + "consumer_accept_list.0": "tf-test8v1bd04pxa", + "consumer_accept_list.1": "12345", + }, + After: map[string]interface{}{ + "consumer_accept_list.#": 2, + "consumer_accept_list.0": "tf-test8v1bd04pxa", + "consumer_accept_list.1": "12345", + }, + }, + { + Name: "projects are empty", + KeysToSuppress: []string{}, + Before: map[string]interface{}{}, + After: map[string]interface{}{}, + }, + { + Name: "projects have the different length", + KeysToSuppress: []string{}, + Before: map[string]interface{}{}, + After: map[string]interface{}{ + "consumer_accept_list.#": 2, + "consumer_accept_list.0": "tf-test8v1bd04pxa", + "consumer_accept_list.1": "12345", + }, + }, +} + +func (tc *ApigeeInstanceDiffSuppressTestCase) Test(t *testing.T) { + mockResourceDiff := &ResourceDiffMock{ + Before: tc.Before, + After: tc.After, + } + + keysHavingDiff := map[string]bool{} + + for key, val1 := range tc.Before { + val2, ok := tc.After[key] + if !ok { + keysHavingDiff[key] = true + } else if val1 != val2 { + keysHavingDiff[key] = true + } + } + + for key, val1 := range tc.After { + val2, ok := tc.Before[key] + if !ok { + keysHavingDiff[key] = true + } else if val1 != val2 { + keysHavingDiff[key] = true + } + } + + keySuppressionMap := map[string]bool{} + for key := range tc.Before { + keySuppressionMap[key] = false + } + for key := range tc.After { + keySuppressionMap[key] = false + } + + for _, key := range tc.KeysToSuppress { + keySuppressionMap[key] = true + } + + for key := range keysHavingDiff { + actual := projectListDiffSuppressFunc(mockResourceDiff) + if actual != keySuppressionMap[key] { + t.Errorf("Test %s: expected key `%s` to be suppressed", tc.Name, key) + } + } +}