Skip to content

Commit

Permalink
Fix missing schema definition in terraform state (#12039)
Browse files Browse the repository at this point in the history
Add support for Swagger based schema definitions.

Additional changes:

add checks in test that verify, that state includes requested schema
wait in resourceApiManagementApiSchemaCreateUpdate until resource is created to avoid race with following GET, which results in inconsistent state (resource created but not present in state)
add suppress.JsonDiff for suppression of non-essential changes in JSON's
Fixes #12002.
  • Loading branch information
wiktorn authored Jun 17, 2021
1 parent 76329f9 commit 0df0c35
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package apimanagement

import (
"encoding/json"
"fmt"
"log"
"time"
Expand Down Expand Up @@ -52,6 +53,12 @@ func resourceApiManagementApiSchema() *pluginsdk.Resource {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
DiffSuppressFunc: func(k, old, new string, d *pluginsdk.ResourceData) bool {
if d.Get("content_type") == "application/vnd.ms-azure-apim.swagger.definitions+json" || d.Get("content_type") == "application/vnd.oai.openapi.components+json" {
return pluginsdk.SuppressJsonDiff(k, old, new, d)
}
return old == new
},
},
},
}
Expand Down Expand Up @@ -95,15 +102,23 @@ func resourceApiManagementApiSchemaCreateUpdate(d *pluginsdk.ResourceData, meta
return fmt.Errorf("creating or updating API Schema %q (API Management Service %q / API %q / Resource Group %q): %s", schemaID, serviceName, apiName, resourceGroup, err)
}

resp, err := client.Get(ctx, resourceGroup, serviceName, apiName, schemaID)
err := pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError {
resp, err := client.Get(ctx, resourceGroup, serviceName, apiName, schemaID)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return pluginsdk.RetryableError(fmt.Errorf("Expected schema %q (API Management Service %q / API %q / Resource Group %q) to be created but was in non existent state, retrying", schemaID, serviceName, apiName, resourceGroup))
}
return pluginsdk.NonRetryableError(fmt.Errorf("Error getting schema %q (API Management Service %q / API %q / Resource Group %q): %+v", schemaID, serviceName, apiName, resourceGroup, err))
}
if resp.ID == nil {
return pluginsdk.NonRetryableError(fmt.Errorf("Cannot read ID for API Schema %q (API Management Service %q / API %q / Resource Group %q): %s", schemaID, serviceName, apiName, resourceGroup, err))
}
d.SetId(*resp.ID)
return nil
})
if err != nil {
return fmt.Errorf("retrieving API Schema %q (API Management Service %q / API %q / Resource Group %q): %s", schemaID, serviceName, apiName, resourceGroup, err)
return fmt.Errorf("Error getting schema %q (API Management Service %q / API %q / Resource Group %q): %+v", schemaID, serviceName, apiName, resourceGroup, err)
}
if resp.ID == nil {
return fmt.Errorf("Cannot read ID for API Schema %q (API Management Service %q / API %q / Resource Group %q): %s", schemaID, serviceName, apiName, resourceGroup, err)
}
d.SetId(*resp.ID)

return resourceApiManagementApiSchemaRead(d, meta)
}

Expand Down Expand Up @@ -140,10 +155,33 @@ func resourceApiManagementApiSchemaRead(d *pluginsdk.ResourceData, meta interfac
if properties := resp.SchemaContractProperties; properties != nil {
d.Set("content_type", properties.ContentType)
if documentProperties := properties.SchemaDocumentProperties; documentProperties != nil {
d.Set("value", documentProperties.Value)
/*
As per https://docs.microsoft.com/en-us/rest/api/apimanagement/2019-12-01/api-schema/get#schemacontract
- Swagger Schema use application/vnd.ms-azure-apim.swagger.definitions+json
- WSDL Schema use application/vnd.ms-azure-apim.xsd+xml
- OpenApi Schema use application/vnd.oai.openapi.components+json
- WADL Schema use application/vnd.ms-azure-apim.wadl.grammars+xml.
Definitions used for Swagger/OpenAPI schemas only, otherwise Value is used
*/
switch *properties.ContentType {
case "application/vnd.ms-azure-apim.swagger.definitions+json", "application/vnd.oai.openapi.components+json":
if documentProperties.Definitions != nil {
value, err := json.Marshal(documentProperties.Definitions)
if err != nil {
return fmt.Errorf("[FATAL] Unable to serialize schema to json. Error: %+v. Schema struct: %+v", err, documentProperties.Definitions)
}
d.Set("value", string(value))
}
case "application/vnd.ms-azure-apim.xsd+xml", "application/vnd.ms-azure-apim.wadl.grammars+xml":
d.Set("value", documentProperties.Value)
default:
log.Printf("[WARN] Unknown content type %q for schema %q (API Management Service %q / API %q / Resource Group %q)", *properties.ContentType, schemaID, serviceName, apiName, resourceGroup)
d.Set("value", documentProperties.Value)
}
}
}

return nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package apimanagement_test
import (
"context"
"fmt"
"io/ioutil"
"strings"
"testing"

"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
Expand All @@ -19,12 +21,31 @@ type ApiManagementApiSchemaResource struct {
func TestAccApiManagementApiSchema_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_api_management_api_schema", "test")
r := ApiManagementApiSchemaResource{}
schema, _ := ioutil.ReadFile("testdata/api_management_api_schema.xml")

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("value").HasValue(string(schema)),
),
},
data.ImportStep(),
})
}

func TestAccApiManagementApiSchema_basicSwagger(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_api_management_api_schema", "test")
r := ApiManagementApiSchemaResource{}
schema, _ := ioutil.ReadFile("testdata/api_management_api_schema_swagger.json")

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basicSwagger(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("value").HasValue(strings.TrimRight(string(schema), "\r\n")),
),
},
data.ImportStep(),
Expand Down Expand Up @@ -79,6 +100,21 @@ resource "azurerm_api_management_api_schema" "test" {
`, r.template(data), data.RandomInteger)
}

func (r ApiManagementApiSchemaResource) basicSwagger(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_api_management_api_schema" "test" {
api_name = azurerm_api_management_api.test.name
api_management_name = azurerm_api_management_api.test.api_management_name
resource_group_name = azurerm_api_management_api.test.resource_group_name
schema_id = "acctestSchema%d"
content_type = "application/vnd.ms-azure-apim.swagger.definitions+json"
value = file("testdata/api_management_api_schema_swagger.json")
}
`, r.template(data), data.RandomInteger)
}

func (r ApiManagementApiSchemaResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"schema-bug-example":{"properties":{"Field2":{"description":"Field2","type":"string"},"field1":{"description":"Field1","type":"string"}},"required":["field1","Field2"],"type":"object"}}

0 comments on commit 0df0c35

Please sign in to comment.