-
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 #1672 from mapped/add-virtual-networks
Add support for virtual networks
- Loading branch information
Showing
8 changed files
with
384 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_tunnel_virtual_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,50 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "cloudflare_tunnel_virtual_network Resource - Cloudflare" | ||
subcategory: "" | ||
description: |- | ||
Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel | ||
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to | ||
handle overlapping private IPs in your origins. | ||
--- | ||
|
||
# cloudflare_tunnel_virtual_network (Resource) | ||
|
||
Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel | ||
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to | ||
handle overlapping private IPs in your origins. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "cloudflare_tunnel_virtual_network" "example" { | ||
account_id = "c4a7362d577a6c3019a474fd6f485821" | ||
name = "vnet-for-documentation" | ||
comment = "New tunnel virtual network for documentation" | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `account_id` (String) The account identifier to target for the resource. | ||
- `name` (String) A user-friendly name chosen when the virtual network is created. | ||
|
||
### Optional | ||
|
||
- `comment` (String) Description of the tunnel virtual network. | ||
- `is_default_network` (Boolean) Whether this virtual network is the default one for the account. This means IP Routes belong to this virtual network and Teams Clients in the account route through this virtual network, unless specified otherwise for each case. | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
## Import | ||
|
||
Import is supported using the following syntax: | ||
|
||
```shell | ||
terraform import cloudflare_tunnel_virtual_network c4a7362d577a6c3019a474fd6f485821/3c8ff8af-b487-45bd-89e3-4c85a1532600 | ||
``` |
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 @@ | ||
$ terraform import cloudflare_tunnel_virtual_network c4a7362d577a6c3019a474fd6f485821/3c8ff8af-b487-45bd-89e3-4c85a1532600 |
5 changes: 5 additions & 0 deletions
5
examples/resources/cloudflare_tunnel_virtual_network/resource.tf
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,5 @@ | ||
resource "cloudflare_tunnel_virtual_network" "example" { | ||
account_id = "c4a7362d577a6c3019a474fd6f485821" | ||
name = "vnet-for-documentation" | ||
comment = "New tunnel virtual network for documentation" | ||
} |
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
142 changes: 142 additions & 0 deletions
142
internal/provider/resource_cloudflare_tunnel_virtual_network.go
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,142 @@ | ||
package provider | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"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" | ||
) | ||
|
||
func resourceCloudflareTunnelVirtualNetwork() *schema.Resource { | ||
return &schema.Resource{ | ||
Schema: resourceCloudflareTunnelVirtualNetworkSchema(), | ||
CreateContext: resourceCloudflareTunnelVirtualNetworkCreate, | ||
ReadContext: resourceCloudflareTunnelVirtualNetworkRead, | ||
UpdateContext: resourceCloudflareTunnelVirtualNetworkUpdate, | ||
DeleteContext: resourceCloudflareTunnelVirtualNetworkDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: resourceCloudflareTunnelVirtualNetworkImport, | ||
}, | ||
Description: ` | ||
Provides a resource, that manages Cloudflare tunnel virtual networks for Zero Trust. Tunnel | ||
virtual networks are used for segregation of Tunnel IP Routes via Virtualized Networks to | ||
handle overlapping private IPs in your origins.`, | ||
} | ||
} | ||
|
||
func resourceCloudflareTunnelVirtualNetworkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
accountID := d.Get("account_id").(string) | ||
|
||
tunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(ctx, cloudflare.TunnelVirtualNetworksListParams{ | ||
AccountID: accountID, | ||
IsDeleted: cloudflare.BoolPtr(false), | ||
ID: d.Id(), | ||
}) | ||
|
||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("failed to fetch Tunnel Virtual Network: %w", err)) | ||
} | ||
|
||
if len(tunnelVirtualNetworks) < 1 { | ||
tflog.Info(ctx, fmt.Sprintf("Tunnel Virtual Network for ID %s in account %s not found", d.Id(), accountID)) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
tunnelVirtualNetwork := tunnelVirtualNetworks[0] | ||
|
||
d.Set("name", tunnelVirtualNetwork.Name) | ||
d.Set("is_default_network", tunnelVirtualNetwork.IsDefaultNetwork) | ||
|
||
if len(tunnelVirtualNetwork.Comment) > 0 { | ||
d.Set("comment", tunnelVirtualNetwork.Comment) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareTunnelVirtualNetworkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
name := d.Get("name").(string) | ||
|
||
resource := cloudflare.TunnelVirtualNetworkCreateParams{ | ||
AccountID: d.Get("account_id").(string), | ||
Name: name, | ||
IsDefault: d.Get("is_default_network").(bool), | ||
} | ||
|
||
if comment, ok := d.Get("comment").(string); ok { | ||
resource.Comment = comment | ||
} | ||
|
||
newTunnelVirtualNetwork, err := client.CreateTunnelVirtualNetwork(ctx, resource) | ||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("error creating Tunnel Virtual Network %q: %w", name, err)) | ||
} | ||
|
||
d.SetId(newTunnelVirtualNetwork.ID) | ||
|
||
return resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta) | ||
} | ||
|
||
func resourceCloudflareTunnelVirtualNetworkUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
|
||
resource := cloudflare.TunnelVirtualNetworkUpdateParams{ | ||
AccountID: d.Get("account_id").(string), | ||
Name: d.Get("name").(string), | ||
IsDefaultNetwork: cloudflare.BoolPtr(d.Get("is_default_network").(bool)), | ||
VnetID: d.Id(), | ||
} | ||
|
||
if comment, ok := d.Get("comment").(string); ok { | ||
resource.Comment = comment | ||
} | ||
|
||
_, err := client.UpdateTunnelVirtualNetwork(ctx, resource) | ||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("error updating Tunnel Virtual Network %q: %w", d.Id(), err)) | ||
} | ||
|
||
return resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta) | ||
} | ||
|
||
func resourceCloudflareTunnelVirtualNetworkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
|
||
err := client.DeleteTunnelVirtualNetwork(ctx, cloudflare.TunnelVirtualNetworkDeleteParams{ | ||
AccountID: d.Get("account_id").(string), | ||
VnetID: d.Id(), | ||
}) | ||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("error deleting Tunnel Virtual Network %q: %w", d.Id(), err)) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceCloudflareTunnelVirtualNetworkImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { | ||
attributes := strings.SplitN(d.Id(), "/", 2) | ||
|
||
if len(attributes) != 2 { | ||
return nil, fmt.Errorf(`invalid id (%q) specified, should be in format "accountID/vnetID"`, d.Id()) | ||
} | ||
|
||
accountID, vnetID := attributes[0], attributes[1] | ||
|
||
d.SetId(vnetID) | ||
d.Set("account_id", accountID) | ||
|
||
err := resourceCloudflareTunnelVirtualNetworkRead(ctx, d, meta) | ||
if err != nil { | ||
return nil, errors.New("failed to read Tunnel Virtual Network state") | ||
} | ||
|
||
return []*schema.ResourceData{d}, nil | ||
} |
151 changes: 151 additions & 0 deletions
151
internal/provider/resource_cloudflare_tunnel_virtual_network_test.go
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,151 @@ | ||
package provider | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"log" | ||
"os" | ||
"testing" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func init() { | ||
resource.AddTestSweepers("cloudflare_tunnel_virtual_network", &resource.Sweeper{ | ||
Name: "cloudflare_tunnel_virtual_network", | ||
F: testSweepCloudflareTunnelVirtualNetwork, | ||
}) | ||
} | ||
|
||
func testSweepCloudflareTunnelVirtualNetwork(r string) error { | ||
ctx := context.Background() | ||
client, clientErr := sharedClient() | ||
if clientErr != nil { | ||
tflog.Error(ctx, fmt.Sprintf("Failed to create Cloudflare client: %s", clientErr)) | ||
} | ||
|
||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
if accountID == "" { | ||
return errors.New("CLOUDFLARE_ACCOUNT_ID must be set") | ||
} | ||
|
||
tunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(context.Background(), cloudflare.TunnelVirtualNetworksListParams{AccountID: accountID}) | ||
if err != nil { | ||
tflog.Error(ctx, fmt.Sprintf("Failed to fetch Cloudflare Tunnel Virtual Networks: %s", err)) | ||
} | ||
|
||
if len(tunnelVirtualNetworks) == 0 { | ||
log.Print("[DEBUG] No Cloudflare Tunnel Virtual Networks to sweep") | ||
return nil | ||
} | ||
|
||
for _, vnet := range tunnelVirtualNetworks { | ||
tflog.Info(ctx, fmt.Sprintf("Deleting Cloudflare Tunnel Virtual Network %s", vnet.ID)) | ||
//nolint:errcheck | ||
client.DeleteTunnelVirtualNetwork(context.Background(), cloudflare.TunnelVirtualNetworkDeleteParams{AccountID: accountID, VnetID: vnet.ID}) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func TestAccCloudflareTunnelVirtualNetwork_Exists(t *testing.T) { | ||
rnd := generateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_tunnel_virtual_network.%s", rnd) | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
var TunnelVirtualNetwork cloudflare.TunnelVirtualNetwork | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { | ||
testAccPreCheck(t) | ||
testAccPreCheckAccount(t) | ||
}, | ||
ProviderFactories: providerFactories, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd, accountID, rnd, false), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork), | ||
resource.TestCheckResourceAttr(name, "account_id", accountID), | ||
resource.TestCheckResourceAttr(name, "name", rnd), | ||
resource.TestCheckResourceAttr(name, "comment", rnd), | ||
resource.TestCheckResourceAttr(name, "is_default_network", "false"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckCloudflareTunnelVirtualNetworkExists(name string, virtualNetwork *cloudflare.TunnelVirtualNetwork) 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 Tunnel Virtual Network is set") | ||
} | ||
|
||
client := testAccProvider.Meta().(*cloudflare.API) | ||
foundTunnelVirtualNetworks, err := client.ListTunnelVirtualNetworks(context.Background(), cloudflare.TunnelVirtualNetworksListParams{ | ||
AccountID: rs.Primary.Attributes["account_id"], | ||
IsDeleted: cloudflare.BoolPtr(false), | ||
ID: rs.Primary.ID, | ||
}) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
*virtualNetwork = foundTunnelVirtualNetworks[0] | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func TestAccCloudflareTunnelVirtualNetwork_UpdateComment(t *testing.T) { | ||
rnd := generateRandomResourceName() | ||
name := fmt.Sprintf("cloudflare_tunnel_virtual_network.%s", rnd) | ||
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") | ||
|
||
var TunnelVirtualNetwork cloudflare.TunnelVirtualNetwork | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { | ||
testAccPreCheck(t) | ||
testAccPreCheckAccount(t) | ||
}, | ||
ProviderFactories: providerFactories, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd, accountID, rnd, false), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork), | ||
resource.TestCheckResourceAttr(name, "comment", rnd), | ||
), | ||
}, | ||
{ | ||
Config: testAccCloudflareTunnelVirtualNetworkSimple(rnd, rnd+"-updated", accountID, rnd, false), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckCloudflareTunnelVirtualNetworkExists(name, &TunnelVirtualNetwork), | ||
resource.TestCheckResourceAttr(name, "comment", rnd+"-updated"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCloudflareTunnelVirtualNetworkSimple(ID, comment, accountID, name string, isDefault bool) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_tunnel_virtual_network" "%[1]s" { | ||
account_id = "%[3]s" | ||
name = "%[4]s" | ||
comment = "%[2]s" | ||
is_default_network = "%[5]t" | ||
}`, ID, comment, accountID, name, isDefault) | ||
} |
Oops, something went wrong.