Skip to content

Commit

Permalink
Added support for redirect lists.
Browse files Browse the repository at this point in the history
  • Loading branch information
orium committed Jun 15, 2022
1 parent 143ddc3 commit 438e821
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 29 deletions.
7 changes: 7 additions & 0 deletions .changelog/1700.txt
@@ -0,0 +1,7 @@
```release-note:new-resource
resource/cloudflare_list: Added support for generic list types, including redirect lists.
```

```release-note:note
resource/cloudflare_ip_list: Deprecated cloudflare_ip_list in favor of cloudflare_list.
```
91 changes: 81 additions & 10 deletions internal/provider/resource_cloudflare_list.go
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"strings"

"github.com/cloudflare/cloudflare-go"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand Down Expand Up @@ -42,7 +41,7 @@ func resourceCloudflareListCreate(ctx context.Context, d *schema.ResourceData, m
d.SetId(list.ID)

if items, ok := d.GetOk("item"); ok {
items := buildListItemsCreateRequest(items.(*schema.Set).List())
items := buildListItemsCreateRequest(d, items.([]interface{}))
_, err = client.CreateListItems(ctx, cloudflare.ListCreateItemsParams{
AccountID: accountID,
ID: d.Id(),
Expand Down Expand Up @@ -106,9 +105,25 @@ func resourceCloudflareListRead(ctx context.Context, d *schema.ResourceData, met

for _, i := range items {
item = make(map[string]interface{})
item["value"] = []map[string]interface{}{
{"ip": i.IP},

value := make(map[string]interface{})

if i.IP != nil {
value["ip"] = *i.IP
}
if i.Redirect != nil {
value["redirect"] = []map[string]interface{}{{
"source_url": i.Redirect.SourceUrl,
"include_subdomains": i.Redirect.IncludeSubdomains,
"target_url": i.Redirect.TargetUrl,
"status_code": i.Redirect.StatusCode,
"preserve_query_string": i.Redirect.PreserveQueryString,
"subpath_matching": i.Redirect.SubpathMatching,
"preserve_path_suffix": i.Redirect.PreservePathSuffix,
}}
}

item["value"] = []map[string]interface{}{value}
item["comment"] = i.Comment

itemData = append(itemData, item)
Expand All @@ -133,7 +148,7 @@ func resourceCloudflareListUpdate(ctx context.Context, d *schema.ResourceData, m
}

if items, ok := d.GetOk("item"); ok {
items := buildListItemsCreateRequest(items.(*schema.Set).List())
items := buildListItemsCreateRequest(d, items.([]interface{}))
_, err = client.ReplaceListItems(ctx, cloudflare.ListReplaceItemsParams{
AccountID: accountID,
ID: d.Id(),
Expand Down Expand Up @@ -162,15 +177,71 @@ func resourceCloudflareListDelete(ctx context.Context, d *schema.ResourceData, m
return nil
}

func buildListItemsCreateRequest(items []interface{}) []cloudflare.ListItemCreateRequest {
func buildListItemsCreateRequest(resource *schema.ResourceData, items []interface{}) []cloudflare.ListItemCreateRequest {
var listItems []cloudflare.ListItemCreateRequest

for _, item := range items {
value := item.(map[string]interface{})["value"].([]interface{})[0]
for i, item := range items {
value := item.(map[string]interface{})["value"].([]interface{})[0].(map[string]interface{})

_, hasIP := resource.GetOkExists(fmt.Sprintf("item.%d.value.0.ip", i))

var ip *string = nil
if hasIP {
maybeIP := value["ip"].(string)
ip = &maybeIP
}

_, hasRedirect := resource.GetOkExists(fmt.Sprintf("item.%d.value.0.redirect", i))

var redirect *cloudflare.Redirect = nil
if hasRedirect {
r := value["redirect"].([]interface{})[0].(map[string]interface{})

sourceUrl := r["source_url"].(string)
targetUrl := r["target_url"].(string)

var includeSubdomains *bool = nil
var subpathMatching *bool = nil
var statusCode *int = nil
var preserveQueryString *bool = nil
var preservePathSuffix *bool = nil

hasField := func(field string) bool {
_, has := resource.GetOkExists(fmt.Sprintf("item.%d.value.0.redirect.0.%s", i, field))
return has
}

if hasField("include_subdomains") {
includeSubdomains = cloudflare.BoolPtr(r["include_subdomains"].(bool))
}
if hasField("subpath_matching") {
subpathMatching = cloudflare.BoolPtr(r["subpath_matching"].(bool))
}
if hasField("status_code") {
statusCode = cloudflare.IntPtr(r["status_code"].(int))
}
if hasField("preserve_query_string") {
preserveQueryString = cloudflare.BoolPtr(r["preserve_query_string"].(bool))
}
if hasField("preserve_path_suffix") {
preservePathSuffix = cloudflare.BoolPtr(r["preserve_path_suffix"].(bool))
}

redirect = &cloudflare.Redirect{
SourceUrl: sourceUrl,
IncludeSubdomains: includeSubdomains,
TargetUrl: targetUrl,
StatusCode: statusCode,
PreserveQueryString: preserveQueryString,
SubpathMatching: subpathMatching,
PreservePathSuffix: preservePathSuffix,
}
}

listItems = append(listItems, cloudflare.ListItemCreateRequest{
IP: cloudflare.StringPtr(value.(map[string]interface{})["ip"].(string)),
Comment: item.(map[string]interface{})["comment"].(string),
IP: ip,
Redirect: redirect,
Comment: item.(map[string]interface{})["comment"].(string),
})
}

Expand Down
95 changes: 79 additions & 16 deletions internal/provider/resource_cloudflare_list_test.go
Expand Up @@ -32,7 +32,7 @@ func TestAccCloudflareList_Exists(t *testing.T) {
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareList(rnd, rnd, rnd, accountID),
Config: testAccCheckCloudflareList(rnd, rnd, rnd, accountID, "ip"),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(name, &list),
resource.TestCheckResourceAttr(
Expand Down Expand Up @@ -65,7 +65,7 @@ func TestAccCloudflareList_UpdateDescription(t *testing.T) {
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareList(rnd, rnd, rnd, accountID),
Config: testAccCheckCloudflareList(rnd, rnd, rnd, accountID, "ip"),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(name, &list),
resource.TestCheckResourceAttr(
Expand All @@ -76,7 +76,7 @@ func TestAccCloudflareList_UpdateDescription(t *testing.T) {
PreConfig: func() {
initialID = list.ID
},
Config: testAccCheckCloudflareList(rnd, rnd, rnd+"-updated", accountID),
Config: testAccCheckCloudflareList(rnd, rnd, rnd+"-updated", accountID, "ip"),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(name, &list),
func(state *terraform.State) error {
Expand All @@ -102,8 +102,12 @@ func TestAccCloudflareList_Update(t *testing.T) {
os.Setenv("CLOUDFLARE_API_TOKEN", "")
}

rnd := generateRandomResourceName()
name := fmt.Sprintf("cloudflare_list.%s", rnd)
rndIP := generateRandomResourceName()
rndRedirect := generateRandomResourceName()

nameIP := fmt.Sprintf("cloudflare_list.%s", rndIP)
nameRedirect := fmt.Sprintf("cloudflare_list.%s", rndRedirect)

accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")

var list cloudflare.List
Expand All @@ -114,27 +118,51 @@ func TestAccCloudflareList_Update(t *testing.T) {
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareList(rnd, rnd, rnd, accountID),
Config: testAccCheckCloudflareList(rndIP, rndIP, rndIP, accountID, "ip"),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(name, &list),
testAccCheckCloudflareListExists(nameIP, &list),
resource.TestCheckResourceAttr(
name, "name", rnd),
nameIP, "name", rndIP),
),
},
{
PreConfig: func() {
initialID = list.ID
},
Config: testAccCheckCloudflareListUpdate(rnd, rnd, rnd, accountID),
Config: testAccCheckCloudflareListIPUpdate(rndIP, rndIP, rndIP, accountID),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(name, &list),
testAccCheckCloudflareListExists(nameIP, &list),
func(state *terraform.State) error {
if initialID != list.ID {
return fmt.Errorf("wanted update but List got recreated (id changed %q -> %q)", initialID, list.ID)
}
return nil
},
resource.TestCheckResourceAttr(nameIP, "item.#", "2"),
),
},
{
Config: testAccCheckCloudflareList(rndRedirect, rndRedirect, rndRedirect, accountID, "redirect"),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(nameRedirect, &list),
resource.TestCheckResourceAttr(
nameRedirect, "name", rndRedirect),
),
},
{
PreConfig: func() {
initialID = list.ID
},
Config: testAccCheckCloudflareListRedirectUpdate(rndRedirect, rndRedirect, rndRedirect, accountID),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudflareListExists(nameRedirect, &list),
func(state *terraform.State) error {
if initialID != list.ID {
return fmt.Errorf("wanted update but List got recreated (id changed %q -> %q)", initialID, list.ID)
}
return nil
},
resource.TestCheckResourceAttr(name, "item.#", "2"),
resource.TestCheckResourceAttr(nameRedirect, "item.#", "2"),
),
},
},
Expand Down Expand Up @@ -168,17 +196,17 @@ func testAccCheckCloudflareListExists(n string, list *cloudflare.List) resource.
}
}

func testAccCheckCloudflareList(ID, name, description, accountID string) string {
func testAccCheckCloudflareList(ID, name, description, accountID, kind string) string {
return fmt.Sprintf(`
resource "cloudflare_list" "%[1]s" {
account_id = "%[4]s"
name = "%[2]s"
description = "%[3]s"
kind = "ip"
}`, ID, name, description, accountID)
kind = "%[5]s"
}`, ID, name, description, accountID, kind)
}

func testAccCheckCloudflareListUpdate(ID, name, description, accountID string) string {
func testAccCheckCloudflareListIPUpdate(ID, name, description, accountID string) string {
return fmt.Sprintf(`
resource "cloudflare_list" "%[1]s" {
account_id = "%[4]s"
Expand All @@ -189,7 +217,7 @@ func testAccCheckCloudflareListUpdate(ID, name, description, accountID string) s
item {
value {
ip = "192.0.2.0"
}
}
comment = "one"
}
Expand All @@ -201,3 +229,38 @@ func testAccCheckCloudflareListUpdate(ID, name, description, accountID string) s
}
}`, ID, name, description, accountID)
}

func testAccCheckCloudflareListRedirectUpdate(ID, name, description, accountID string) string {
return fmt.Sprintf(`
resource "cloudflare_list" "%[1]s" {
account_id = "%[4]s"
name = "%[2]s"
description = "%[3]s"
kind = "redirect"
item {
value {
redirect {
source_url = "cloudflare.com/blog"
target_url = "https://blog.cloudflare.com"
}
}
comment = "one"
}
item {
value {
redirect {
source_url = "cloudflare.com/foo"
target_url = "https://foo.cloudflare.com"
include_subdomains = true
subpath_matching = true
status_code = 301
preserve_query_string = true
preserve_path_suffix = false
}
}
comment = "two"
}
}`, ID, name, description, accountID)
}
42 changes: 39 additions & 3 deletions internal/provider/schema_cloudflare_list.go
Expand Up @@ -26,11 +26,11 @@ func resourceCloudflareListSchema() map[string]*schema.Schema {
},
"kind": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"ip"}, false),
ValidateFunc: validation.StringInSlice([]string{"ip", "redirect"}, false),
Required: true,
},
"item": {
Type: schema.TypeSet,
Type: schema.TypeList,
Optional: true,
Elem: listItemElem,
},
Expand All @@ -48,7 +48,43 @@ var listItemElem = &schema.Resource{
Schema: map[string]*schema.Schema{
"ip": {
Type: schema.TypeString,
Required: true,
Optional: true,
},
"redirect": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"source_url": {
Type: schema.TypeString,
Required: true,
},
"target_url": {
Type: schema.TypeString,
Required: true,
},
"include_subdomains": {
Type: schema.TypeBool,
Optional: true,
},
"subpath_matching": {
Type: schema.TypeBool,
Optional: true,
},
"status_code": {
Type: schema.TypeInt,
Optional: true,
},
"preserve_query_string": {
Type: schema.TypeBool,
Optional: true,
},
"preserve_path_suffix": {
Type: schema.TypeBool,
Optional: true,
},
},
},
},
},
},
Expand Down

0 comments on commit 438e821

Please sign in to comment.