-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
azurerm_api_management_api
: split create and update method
#28271
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @wuxu92, this PR is nearly there but there is an issue with the update because we're using a new payload instead of the existing one. I've left some comments below but happy to answer any questions
|
||
// If import is used, we need to send properties to Azure API in two operations. | ||
// First we execute import and then updated the other props. | ||
if vs, hasImport := d.GetOk("import"); hasImport { | ||
importVs := vs.([]interface{}) | ||
if importVs := d.Get("import").([]interface{}); len(importVs) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should confirm we can cast to .([]interface{})
to prevent a potential panic
if importVs := d.Get("import").([]interface{}); len(importVs) > 0 { | |
if importVs, ok := d.Get("import").([]interface{}); ok && len(importVs) > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Get will return the zero value of the schema if the field is not set. Anyway, I'd add the check here to make it clear.
Lines 565 to 567 in a74f82f
if result.Value == nil && schema != nil { | |
result.Value = result.ValueOrZero(schema) | |
} |
} | ||
} | ||
|
||
prop := &api.ApiCreateOrUpdateProperties{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestAccApiManagementApi_importUpdate
is failing because we're building a new payload rather than using the existing one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I've updated the Update method with existing payload and the acctest updated. some fields can be set by tf configuration or by import
block. so I added an ignore_changes to this acc test.
--- PASS: TestAccApiManagementApi_importUpdate (336.19s)
PASS```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to set every variable in a new struct or could we just use existing
and change any values through d.HasChange
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The data type of existing
is ApiContractProperties
which is not the same as ApiCreateOrUpdateProperties
. so a copy is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh shoot. Thanks for the added context!
path = "butter-parser-update" | ||
protocols = ["https"] | ||
revision = "3" | ||
description = "What is my purpose? You parse butter update." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is changing every updatable attribute, we'll miss a bug in the current update method. I encourage you to keep this value the same and run this test again to see what happens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned, the update method is now working with the existing payload and has been tested successfully.
--- PASS: TestAccApiManagementApi_completeUpdate (389.37s)
PASS
@mbfrahry Could you review this PR again? These comments should have been resolved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your patience on this review. There are some things that won't work quite right the way it's written that I've documented. Let me know if you have any questions
return fmt.Errorf("creating/updating %s: %+v", *id, err) | ||
} | ||
|
||
d.SetId(id.ID()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to reset the ID after we update as it should be the same ID as when we set it during Create. This could cause issues if the IDs between Create and Update aren't the same for whatever reason
} | ||
|
||
func expandApiManagementApiImport(importVs []interface{}, apiType api.ApiType, soapApiType api.SoapApiType, path, serviceUrl, version, versionSetId string) api.ApiCreateOrUpdateParameter { | ||
importV := importVs[0].(map[string]interface{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There aren't any checks that len(importVs) isn't 0 or that importVs[0] isn't nil. We'll want to add those to prevent a potential crash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated. also update the create method with this expand method.
|
||
wsdlSelectorVs := importV["wsdl_selector"].([]interface{}) | ||
if len(wsdlSelectorVs) > 0 { | ||
wsdlSelectorV := wsdlSelectorVs[0].(map[string]interface{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here, we want to check that wsdlSelectorVs[0]
isn't nil
} | ||
|
||
if d.HasChange("version_set_id") { | ||
prop.ApiVersionSetId = &versionSetId |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prop.ApiVersionSetId = &versionSetId | |
prop.ApiVersionSetId = pointer.To(versionSetId) |
sourceApiId := d.Get("source_api_id").(string) | ||
serviceUrl := d.Get("service_url").(string) | ||
|
||
if version != "" && versionSetId == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can reduce code duplication and get the following checks checked at plan time rather than apply time by using doing CustomizeDiff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated.
Plan: 2 to add, 0 to change, 1 to destroy.
│ Error: 1 error occurred:
│ * setting `version` without the required `version_set_id`
│
Plan: 2 to add, 0 to change, 1 to destroy.
╷
│ Error: 1 error occurred:
│ * `display_name`, `protocols` are required when `source_api_id` is not set
subscriptionKeyParameterNamesRaw := d.Get("subscription_key_parameter_names").([]interface{}) | ||
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(subscriptionKeyParameterNamesRaw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
subscriptionKeyParameterNamesRaw := d.Get("subscription_key_parameter_names").([]interface{}) | |
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(subscriptionKeyParameterNamesRaw) | |
prop.SubscriptionKeyParameterNames = expandApiManagementApiSubscriptionKeyParamNames(d.Get("subscription_key_parameter_names").([]interface{})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
oAuth2AuthorizationSettingsRaw := d.Get("oauth2_authorization").([]interface{}) | ||
oAuth2AuthorizationSettings := expandApiManagementOAuth2AuthenticationSettingsContract(oAuth2AuthorizationSettingsRaw) | ||
authenticationSettings.OAuth2 = oAuth2AuthorizationSettings | ||
prop.AuthenticationSettings = authenticationSettings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way this is written will reset openid_authentication
if it doesn't change as it's setting oauth2_authorization
into an empty AuthenticationSettingsContract
We should modify the existing AuthenticationSettings
rather than creating a new one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Updated.
openIDAuthorizationSettingsRaw := d.Get("openid_authentication").([]interface{}) | ||
openIDAuthorizationSettings := expandApiManagementOpenIDAuthenticationSettingsContract(openIDAuthorizationSettingsRaw) | ||
authenticationSettings.Openid = openIDAuthorizationSettings | ||
prop.AuthenticationSettings = authenticationSettings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And the vice versa, this will reset oauth2_authorization
if oauth2_authorization
hasn't changed
We should modify the existing AuthenticationSettings rather than creating a new one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated with above
contactInfoRaw := d.Get("contact").([]interface{}) | ||
prop.Contact = expandApiManagementApiContact(contactInfoRaw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contactInfoRaw := d.Get("contact").([]interface{}) | |
prop.Contact = expandApiManagementApiContact(contactInfoRaw) | |
prop.Contact = expandApiManagementApiContact(d.Get("contact").([]interface{})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
licenseInfoRaw := d.Get("license").([]interface{}) | ||
prop.License = expandApiManagementApiLicense(licenseInfoRaw) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
licenseInfoRaw := d.Get("license").([]interface{}) | |
prop.License = expandApiManagementApiLicense(licenseInfoRaw) | |
prop.License = expandApiManagementApiLicense(d.Get("license").([]interface{})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated.
84bb9e9
to
4a2c639
Compare
@mbfrahry Thanks for the comments! I updated the branch with the main and fixed the corresponding code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @wuxu92, thanks for taking the time to go over my review. I left a couple more comments
ApiVersion: existing.ApiVersion, | ||
ApiVersionSetId: existing.ApiVersionSetId, | ||
TermsOfServiceURL: existing.TermsOfServiceURL, | ||
Type: existing.Type, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does look like ApiType
didn't get copied over. I just want to make sure that was intentional
|
||
CustomizeDiff: pluginsdk.CustomDiffWithAll( | ||
pluginsdk.CustomizeDiffShim(func(ctx context.Context, d *pluginsdk.ResourceDiff, v interface{}) error { | ||
if d.Get("version").(string) != "" && d.Get("version_set_id").(string) == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you saw the errors around
* setting `version` without the required `version_set_id`
That's because version_set_id
is interpolated with azurerm_api_management_api_version_set.test.id
and Terraform doesn't know what an interpolated value is at plan time so it marks it as an empty string.
What you can instead do is use d.GetRawConfig().AsValueMap()["version_set_id"].IsNull()
which Terraform can look to see if it's interpolated or not.
You can make similar changes to the check below as well
Community Note
Description
Split Update method from CreateUpdate method, and an update test for it.
PR Checklist
For example: “
resource_name_here
- description of change e.g. adding propertynew_property_name_here
”Testing