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 22, 2022
1 parent 7f9902f commit cd7c11c
Show file tree
Hide file tree
Showing 4 changed files with 221 additions and 32 deletions.
7 changes: 7 additions & 0 deletions .changelog/1700.txt
Original file line number Diff line number Diff line change
@@ -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.
```
90 changes: 81 additions & 9 deletions internal/provider/resource_cloudflare_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,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 +106,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 +149,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 +178,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
Original file line number Diff line number Diff line change
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)
}
61 changes: 54 additions & 7 deletions internal/provider/schema_cloudflare_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,25 @@ func resourceCloudflareListSchema() map[string]*schema.Schema {
Required: true,
},
"name": {
Description: "The name of the list.",
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringMatch(regexp.MustCompile("^[0-9a-z_]+$"), "List name must only contain lowercase letters, numbers and underscores"),
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "An optional description of the list.",
Type: schema.TypeString,
Optional: true,
},
"kind": {
Description: "The type of items the list will contain.",
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,14 +51,58 @@ 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": {
Description: "The source url of the redirect.",
Type: schema.TypeString,
Required: true,
},
"target_url": {
Description: "The target url of the redirect.",
Type: schema.TypeString,
Required: true,
},
"include_subdomains": {
Description: "Whether the redirect also matches subdomains of the source url.",
Type: schema.TypeBool,
Optional: true,
},
"subpath_matching": {
Description: "Whether the redirect also matches subpaths of the source url.",
Type: schema.TypeBool,
Optional: true,
},
"status_code": {
Description: "The status code to be used when redirecting a request.",
Type: schema.TypeInt,
Optional: true,
},
"preserve_query_string": {
Description: "Whether the redirect target url should keep the query string of the request's url.",
Type: schema.TypeBool,
Optional: true,
},
"preserve_path_suffix": {
Description: "Whether to preserve the path suffix when doing subpath matching.",
Type: schema.TypeBool,
Optional: true,
},
},
},
},
},
},
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "An optional comment for the item.",
Type: schema.TypeString,
Optional: true,
},
},
}

0 comments on commit cd7c11c

Please sign in to comment.