-
Notifications
You must be signed in to change notification settings - Fork 630
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1572 from terinjokes/teams_route
adds cloudflare_teams_route and acceptance tests
- Loading branch information
Showing
7 changed files
with
316 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:new-resource | ||
cloudflare_teams_route | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceCloudflareTeamsRoute() *schema.Resource { | ||
return &schema.Resource{ | ||
Schema: resourceCloudflareTeamsRouteSchema(), | ||
Create: resourceCloudflareTeamsRouteCreate, | ||
Read: resourceCloudflareTeamsRouteRead, | ||
Update: resourceCloudflareTeamsRouteUpdate, | ||
Delete: resourceCloudflareTeamsRouteDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: resourceCloudflareTeamsRouteImport, | ||
}, | ||
} | ||
} | ||
|
||
func resourceCloudflareTeamsRouteRead(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
|
||
tunnelRoute, err := client.GetTunnelRouteForIP(context.Background(), cloudflare.TunnelRoutesForIPParams{ | ||
AccountID: d.Get("account_id").(string), | ||
Network: d.Get("network").(string), | ||
}) | ||
if err != nil { | ||
// FIXME(2022-04-21): Until the API returns a valid v4 compatible envelope, we need to | ||
// check if the error message is related to problems unmarshalling the response _or_ | ||
// an expected not found error. | ||
var notFoundError *cloudflare.NotFoundError | ||
if strings.Contains(err.Error(), "error unmarshalling the JSON response error body") || errors.As(err, ¬FoundError) { | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("error reading Tunnel Route for Network %q: %w", d.Id(), err) | ||
} | ||
|
||
d.Set("tunnel_id", tunnelRoute.TunnelID) | ||
d.Set("network", tunnelRoute.Network) | ||
|
||
if len(tunnelRoute.Comment) > 0 { | ||
d.Set("comment", tunnelRoute.Comment) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareTeamsRouteCreate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
|
||
resource := cloudflare.TunnelRoutesCreateParams{ | ||
AccountID: d.Get("account_id").(string), | ||
TunnelID: d.Get("tunnel_id").(string), | ||
Network: d.Get("network").(string), | ||
} | ||
|
||
if comment, ok := d.Get("comment").(string); ok { | ||
resource.Comment = comment | ||
} | ||
|
||
newTunnelRoute, err := client.CreateTunnelRoute(context.Background(), resource) | ||
if err != nil { | ||
return fmt.Errorf("error creating Tunnel Route for Network %q: %w", d.Get("network").(string), err) | ||
} | ||
|
||
d.SetId(newTunnelRoute.Network) | ||
|
||
return resourceCloudflareTeamsRouteRead(d, meta) | ||
} | ||
|
||
func resourceCloudflareTeamsRouteUpdate(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
|
||
resource := cloudflare.TunnelRoutesUpdateParams{ | ||
AccountID: d.Get("account_id").(string), | ||
TunnelID: d.Get("tunnel_id").(string), | ||
Network: d.Get("network").(string), | ||
} | ||
|
||
if comment, ok := d.Get("comment").(string); ok { | ||
resource.Comment = comment | ||
} | ||
|
||
_, err := client.UpdateTunnelRoute(context.Background(), resource) | ||
if err != nil { | ||
return fmt.Errorf("error updating Tunnel Route for Network %q: %w", d.Get("network").(string), err) | ||
} | ||
|
||
return resourceCloudflareTeamsRouteRead(d, meta) | ||
} | ||
|
||
func resourceCloudflareTeamsRouteDelete(d *schema.ResourceData, meta interface{}) error { | ||
client := meta.(*cloudflare.API) | ||
|
||
err := client.DeleteTunnelRoute(context.Background(), cloudflare.TunnelRoutesDeleteParams{ | ||
AccountID: d.Get("account_id").(string), | ||
Network: d.Get("network").(string), | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("error deleting Tunnel Route for Network %q: %w", d.Get("network").(string), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareTeamsRouteImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
attributes := strings.SplitN(d.Id(), "/", 3) | ||
|
||
if len(attributes) != 2 { | ||
return nil, fmt.Errorf(`invalid id (%q) specified, should be in format "accountID/tunnelID/network"`, d.Id()) | ||
} | ||
|
||
accountID, tunnelID, network := attributes[0], attributes[1], attributes[2] | ||
|
||
d.SetId(network) | ||
d.Set("account_id", accountID) | ||
d.Set("tunnel_id", tunnelID) | ||
d.Set("network", network) | ||
|
||
err := resourceCloudflareTeamsRouteRead(d, meta) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccCloudflareTeamsRouteExists(t *testing.T) { | ||
rnd := generateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_teams_route.%s", rnd) | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
var TunnelRoute cloudflare.TunnelRoute | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheckAccount(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareTeamsRouteSimple(rnd, rnd, accountID, "10.0.0.20/32"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTeamsRouteExists(name, &TunnelRoute), | ||
resource.TestCheckResourceAttr(name, "account_id", accountID), | ||
resource.TestCheckResourceAttrSet(name, "tunnel_id"), | ||
resource.TestCheckResourceAttr(name, "network", "10.0.0.20/32"), | ||
resource.TestCheckResourceAttr(name, "comment", rnd), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckCloudflareTeamsRouteExists(name string, route *cloudflare.TunnelRoute) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[name] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", name) | ||
} | ||
|
||
if rs.Primary.ID == "" { | ||
return errors.New("No Teams route is set") | ||
} | ||
|
||
client := testAccProvider.Meta().(*cloudflare.API) | ||
foundTunnelRoute, err := client.GetTunnelRouteForIP(context.Background(), cloudflare.TunnelRoutesForIPParams{ | ||
Network: rs.Primary.ID, | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
*route = foundTunnelRoute | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func TestAccCloudflareTeamsRouteUpdateComment(t *testing.T) { | ||
rnd := generateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_teams_route.%s", rnd) | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
var TunnelRoute cloudflare.TunnelRoute | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheckAccount(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareTeamsRouteSimple(rnd, rnd, accountID, "10.0.0.10/32"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTeamsRouteExists(name, &TunnelRoute), | ||
resource.TestCheckResourceAttr(name, "comment", rnd), | ||
), | ||
}, | ||
{ | ||
Config: testAccCloudflareTeamsRouteSimple(rnd, rnd+"-updated", accountID, "10.0.0.10/32"), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTeamsRouteExists(name, &TunnelRoute), | ||
resource.TestCheckResourceAttr(name, "comment", rnd+"-updated"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCloudflareTeamsRouteSimple(ID, comment, accountID, network string) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_argo_tunnel" "%[1]s" { | ||
account_id = "%[3]s" | ||
name = "%[1]s" | ||
secret = "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=" | ||
} | ||
resource "cloudflare_teams_route" "%[1]s" { | ||
account_id = "%[3]s" | ||
tunnel_id = cloudflare_argo_tunnel.%[1]s.id | ||
network = "%[4]s" | ||
comment = "%[2]s" | ||
}`, ID, comment, accountID, network) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceCloudflareTeamsRouteSchema() map[string]*schema.Schema { | ||
return map[string]*schema.Schema{ | ||
"account_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"tunnel_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"network": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"comment": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
--- | ||
layout: "cloudflare" | ||
page_title: "Cloudflare: cloudflare_teams_route" | ||
sidebar_content: "docs-cloudflare-teams-route" | ||
description: |- | ||
Provides a resource which manages Cloudflare Tunnel Routes for Zero Trust | ||
--- | ||
|
||
# cloudflare_teams_route | ||
|
||
Provides a resource, that manages Cloudflare tunnel routes for Zero Trust. Tunnel | ||
routes are used to direct IP traffic through Cloudflare Tunnels. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "cloudflare_teams_route" "example" | ||
account_id = "c4a7362d577a6c3019a474fd6f485821" | ||
tunnel_id = "f70ff985-a4ef-4643-bbbc-4a0ed4fc8415" | ||
network = "192.0.2.24/32" | ||
comment = "New tunnel route for documentation" | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `account_id` - (Required) The ID of the account where the tunnel route is being created | ||
* `tunnel_id` - (Required) The ID of the tunnel that will service the tunnel route | ||
* `network` - (Required) The IPv4 or IPv6 network that should use this tunnel route, in CIDR notation | ||
* `comment` - (Optional) Description of the tunnel route | ||
|
||
## Import | ||
|
||
An existing tunnel route can be imported using the account ID, tunnel ID, and network CIDR. | ||
|
||
``` | ||
$ terraform import cloudflare_teams_route c4a7362d577a6c3019a474fd6f485821/f70ff985-a4ef-4643-bbbc-4a0ed4fc8415/192.0.2.24/32 | ||
``` |