Skip to content

Commit

Permalink
r/qpi_management: support for policies
Browse files Browse the repository at this point in the history
porting in the changes from #3145 into the APIM resource
  • Loading branch information
tombuildsstuff committed Apr 1, 2019
1 parent cc9372a commit 6caa2e6
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 11 deletions.
5 changes: 5 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ type ArmClient struct {
apiManagementGroupUsersClient apimanagement.GroupUserClient
apiManagementLoggerClient apimanagement.LoggerClient
apiManagementOpenIdConnectClient apimanagement.OpenIDConnectProviderClient
apiManagementPolicyClient apimanagement.PolicyClient
apiManagementProductsClient apimanagement.ProductClient
apiManagementProductApisClient apimanagement.ProductAPIClient
apiManagementProductGroupsClient apimanagement.ProductGroupClient
Expand Down Expand Up @@ -532,6 +533,10 @@ func (c *ArmClient) registerApiManagementServiceClients(endpoint, subscriptionId
c.configureClient(&loggerClient.Client, auth)
c.apiManagementLoggerClient = loggerClient

policyClient := apimanagement.NewPolicyClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&policyClient.Client, auth)
c.apiManagementPolicyClient = policyClient

serviceClient := apimanagement.NewServiceClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&serviceClient.Client, auth)
c.apiManagementServiceClient = serviceClient
Expand Down
47 changes: 47 additions & 0 deletions azurerm/helpers/suppress/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package suppress

import (
"encoding/xml"
"io"
"reflect"
"strings"

"github.com/hashicorp/terraform/helper/schema"
)

func XmlDiff(_, old, new string, _ *schema.ResourceData) bool {
oldTokens, err := expandXmlTokensFromString(old)
if err != nil {
return false
}

newTokens, err := expandXmlTokensFromString(new)
if err != nil {
return false
}

return reflect.DeepEqual(oldTokens, newTokens)
}

// This function will extract all XML tokens from a string, but ignoring all white-space tokens
func expandXmlTokensFromString(input string) ([]xml.Token, error) {
decoder := xml.NewDecoder(strings.NewReader(input))
tokens := make([]xml.Token, 0)
for {
token, err := decoder.Token()
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
if chars, ok := token.(xml.CharData); ok {
text := string(chars)
if strings.TrimSpace(text) == "" {
continue
}
}
tokens = append(tokens, xml.CopyToken(token))
}
return tokens, nil
}
81 changes: 81 additions & 0 deletions azurerm/helpers/suppress/xml_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package suppress

import "testing"

func TestXmlDiff(t *testing.T) {
cases := []struct {
Name string
XmlA string
XmlB string
Suppress bool
}{
{
Name: "empty",
XmlA: "",
XmlB: "",
Suppress: true,
},
{
Name: "neither are xml",
XmlA: "this is not an xml",
XmlB: "neither is this",
Suppress: false,
},
{
Name: "identical texts",
XmlA: "this is not an xml",
XmlB: "this is not an xml",
Suppress: true,
},
{
Name: "xml vs text",
XmlA: "<r></r>",
XmlB: "this is not an xml",
Suppress: false,
},
{
Name: "text vs xml",
XmlA: "this is not an xml",
XmlB: "<r></r>",
Suppress: false,
},
{
Name: "identical xml",
XmlA: "<r><c></c></r>",
XmlB: "<r><c></c></r>",
Suppress: true,
},
{
Name: "xml with different line endings",
XmlA: "<r>\n<c>\n</c>\n</r>",
XmlB: "<r>\r\n<c>\r\n</c>\r\n</r>",
Suppress: true,
},
{
Name: "xml with different indentations",
XmlA: "<r>\n <c>\n </c>\n</r>",
XmlB: "<r>\r\n\t<c>\r\n\t</c>\r\n</r>",
Suppress: true,
},
{
Name: "xml with different quotation marks",
XmlA: "<r><c attr=\"test\"></c></r>",
XmlB: "<r>\r\n\t<c attr='test'>\r\n\t</c>\r\n</r>",
Suppress: true,
},
{
Name: "xml with different spaces",
XmlA: "<r><c attr = 'test'></c></r>",
XmlB: "<r>\r\n\t<c attr='test'>\r\n\t</c>\r\n</r>",
Suppress: true,
},
}

for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
if XmlDiff("test", tc.XmlA, tc.XmlB, nil) != tc.Suppress {
t.Fatalf("Expected XmlDiff to return %t for '%q' == '%q'", tc.Suppress, tc.XmlA, tc.XmlB)
}
})
}
}
114 changes: 114 additions & 0 deletions azurerm/resource_arm_api_management.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
Expand Down Expand Up @@ -255,6 +256,30 @@ func resourceArmApiManagementService() *schema.Resource {
},
},

"policy": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"xml_content": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ConflictsWith: []string{"policy.0.xml_link"},
DiffSuppressFunc: suppress.XmlDiff,
},

"xml_link": {
Type: schema.TypeString,
Optional: true,
ConflictsWith: []string{"policy.0.xml_content"},
},
},
},
},

"sign_in": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -431,6 +456,28 @@ func resourceArmApiManagementServiceCreateUpdate(d *schema.ResourceData, meta in
return fmt.Errorf("Error setting Sign Up settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
}

policyClient := meta.(*ArmClient).apiManagementPolicyClient
policiesRaw := d.Get("policy").([]interface{})
policy, err := expandApiManagementPolicies(policiesRaw)
if err != nil {
return err
}

if d.HasChange("policy") {
if policy == nil {
// remove the existing policy
if resp, err := policyClient.Delete(ctx, resourceGroup, name, ""); err != nil {
if !utils.ResponseWasNotFound(resp) {
return fmt.Errorf("Error removing Policies from API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}
} else {
if _, err := policyClient.CreateOrUpdate(ctx, resourceGroup, name, *policy); err != nil {
return fmt.Errorf("Error setting Policies for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}
}

return resourceArmApiManagementServiceRead(d, meta)
}

Expand Down Expand Up @@ -469,6 +516,14 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error retrieving Sign Up Settings for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
}

policyClient := meta.(*ArmClient).apiManagementPolicyClient
policy, err := policyClient.Get(ctx, resourceGroup, name)
if err != nil {
if !utils.ResponseWasNotFound(policy.Response) {
return fmt.Errorf("Error retrieving Policy for API Management Service %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

d.Set("name", name)
d.Set("resource_group_name", resourceGroup)

Expand Down Expand Up @@ -520,6 +575,10 @@ func resourceArmApiManagementServiceRead(d *schema.ResourceData, meta interface{

flattenAndSetTags(d, resp.Tags)

if err := d.Set("policy", flattenApiManagementPolicies(d, policy)); err != nil {
return fmt.Errorf("Error setting `policy`: %+v", err)
}

return nil
}

Expand Down Expand Up @@ -1045,3 +1104,58 @@ func flattenApiManagementSignUpSettings(input apimanagement.PortalSignupSettings
},
}
}

func expandApiManagementPolicies(input []interface{}) (*apimanagement.PolicyContract, error) {
if len(input) == 0 {
return nil, nil
}

vs := input[0].(map[string]interface{})
xmlContent := vs["xml_content"].(string)
xmlLink := vs["xml_link"].(string)

if xmlContent != "" {
return &apimanagement.PolicyContract{
PolicyContractProperties: &apimanagement.PolicyContractProperties{
ContentFormat: apimanagement.XML,
PolicyContent: utils.String(xmlContent),
},
}, nil
}

if xmlLink != "" {
return &apimanagement.PolicyContract{
PolicyContractProperties: &apimanagement.PolicyContractProperties{
ContentFormat: apimanagement.XMLLink,
PolicyContent: utils.String(xmlLink),
},
}, nil
}

return nil, fmt.Errorf("Either `xml_content` or `xml_link` should be set if the `policy` block is defined.")
}

func flattenApiManagementPolicies(d *schema.ResourceData, input apimanagement.PolicyContract) []interface{} {
output := map[string]interface{}{
"xml_content": "",
"xml_link": "",
}

if props := input.PolicyContractProperties; props != nil {
if props.PolicyContent != nil {
output["xml_content"] = *props.PolicyContent
}
}

// when you submit an `xml_link` to the API, the API downloads this link and stores it as `xml_content`
// as such we need to retrieve this value from the state if it's present
if existing, ok := d.GetOk("policy"); ok {
existingVs := existing.([]interface{})
if len(existingVs) > 0 {
existingV := existingVs[0].(map[string]interface{})
output["xml_link"] = existingV["xml_link"].(string)
}
}

return []interface{}{output}
}
Loading

0 comments on commit 6caa2e6

Please sign in to comment.