From ae06159dd8d109c401ec718bf6b55464e23a20bf Mon Sep 17 00:00:00 2001 From: Tim Obezuk Date: Tue, 11 Apr 2023 20:04:49 -0700 Subject: [PATCH 1/3] Added optional config_src parameter to cloudflare_tunnel resource --- .changelog/2369.txt | 3 + docs/resources/tunnel.md | 1 + .../resource_cloudflare_tunnel.go | 3 +- .../resource_cloudflare_tunnel_test.go | 80 +++++++++++++++++++ .../sdkv2provider/schema_cloudflare_tunnel.go | 7 ++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 .changelog/2369.txt diff --git a/.changelog/2369.txt b/.changelog/2369.txt new file mode 100644 index 0000000000..1368b1c001 --- /dev/null +++ b/.changelog/2369.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/cloudflare_tunnel: Adds config_src parameter +``` \ No newline at end of file diff --git a/docs/resources/tunnel.md b/docs/resources/tunnel.md index 19be7bdfb5..90b9014d55 100644 --- a/docs/resources/tunnel.md +++ b/docs/resources/tunnel.md @@ -30,6 +30,7 @@ resource "cloudflare_tunnel" "example" { - `account_id` (String) The account identifier to target for the resource. **Modifying this attribute will force creation of a new resource.** - `name` (String) A user-friendly name chosen when the tunnel is created. **Modifying this attribute will force creation of a new resource.** - `secret` (String, Sensitive) 32 or more bytes, encoded as a base64 string. The Create Argo Tunnel endpoint sets this as the tunnel's password. Anyone wishing to run the tunnel needs this password. **Modifying this attribute will force creation of a new resource.** +- `config_src` (String, Optional) Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using `tunnel_config`, `tunnel_route` or `tunnel_virtual_network` resources. **Modifying this attribute will force creation of a new resource.** ### Read-Only diff --git a/internal/sdkv2provider/resource_cloudflare_tunnel.go b/internal/sdkv2provider/resource_cloudflare_tunnel.go index 89cbd6bb07..de20ac71b7 100644 --- a/internal/sdkv2provider/resource_cloudflare_tunnel.go +++ b/internal/sdkv2provider/resource_cloudflare_tunnel.go @@ -38,8 +38,9 @@ func resourceCloudflareTunnelCreate(ctx context.Context, d *schema.ResourceData, accID := d.Get(consts.AccountIDSchemaKey).(string) name := d.Get("name").(string) secret := d.Get("secret").(string) + configSrc := d.Get("config_src").(string) - tunnel, err := client.CreateTunnel(ctx, cloudflare.AccountIdentifier(accID), cloudflare.TunnelCreateParams{Name: name, Secret: secret}) + tunnel, err := client.CreateTunnel(ctx, cloudflare.AccountIdentifier(accID), cloudflare.TunnelCreateParams{Name: name, Secret: secret, ConfigSrc: configSrc}) if err != nil { return diag.FromErr(errors.Wrap(err, fmt.Sprintf("failed to create Argo Tunnel"))) } diff --git a/internal/sdkv2provider/resource_cloudflare_tunnel_test.go b/internal/sdkv2provider/resource_cloudflare_tunnel_test.go index f923be8c7f..3ca9d8d2fe 100644 --- a/internal/sdkv2provider/resource_cloudflare_tunnel_test.go +++ b/internal/sdkv2provider/resource_cloudflare_tunnel_test.go @@ -52,6 +52,86 @@ func testAccCheckCloudflareTunnelBasic(accID, name string) string { }`, accID, name) } +func TestAccCloudflareTunnelCreate_Managed(t *testing.T) { + // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Argo Tunnel + // endpoint does not yet support the API tokens. + if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { + t.Setenv("CLOUDFLARE_API_TOKEN", "") + } + + accID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + rnd := generateRandomResourceName() + name := fmt.Sprintf("cloudflare_tunnel.%s", rnd) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckCloudflareTunnelDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareTunnelManaged(accID, rnd), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "name", rnd), + resource.TestCheckResourceAttr(name, "secret", "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg="), + resource.TestMatchResourceAttr(name, "cname", regexp.MustCompile(".*\\.cfargotunnel\\.com")), + ), + }, + }, + }) +} + +func testAccCheckCloudflareTunnelManaged(accID, name string) string { + return fmt.Sprintf(` + resource "cloudflare_tunnel" "%[2]s" { + account_id = "%[1]s" + name = "%[2]s" + secret = "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=" + config_src = "cloudflare" + }`, accID, name) +} + +func TestAccCloudflareTunnelCreate_Unmanaged(t *testing.T) { + // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Argo Tunnel + // endpoint does not yet support the API tokens. + if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { + t.Setenv("CLOUDFLARE_API_TOKEN", "") + } + + accID := os.Getenv("CLOUDFLARE_ACCOUNT_ID") + rnd := generateRandomResourceName() + name := fmt.Sprintf("cloudflare_tunnel.%s", rnd) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckCloudflareTunnelDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckCloudflareTunnelUnmanaged(accID, rnd), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "name", rnd), + resource.TestCheckResourceAttr(name, "secret", "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg="), + resource.TestMatchResourceAttr(name, "cname", regexp.MustCompile(".*\\.cfargotunnel\\.com")), + ), + }, + }, + }) +} + +func testAccCheckCloudflareTunnelUnmanaged(accID, name string) string { + return fmt.Sprintf(` + resource "cloudflare_tunnel" "%[2]s" { + account_id = "%[1]s" + name = "%[2]s" + secret = "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg=" + config_src = "local" + }`, accID, name) +} + func testAccCheckCloudflareTunnelDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "cloudflare_tunnel" { diff --git a/internal/sdkv2provider/schema_cloudflare_tunnel.go b/internal/sdkv2provider/schema_cloudflare_tunnel.go index ae643cda95..9df0dd514c 100644 --- a/internal/sdkv2provider/schema_cloudflare_tunnel.go +++ b/internal/sdkv2provider/schema_cloudflare_tunnel.go @@ -26,6 +26,13 @@ func resourceCloudflareTunnelSchema() map[string]*schema.Schema { ForceNew: true, Description: "32 or more bytes, encoded as a base64 string. The Create Argo Tunnel endpoint sets this as the tunnel's password. Anyone wishing to run the tunnel needs this password.", }, + "config_src": { + Type: schema.TypeString, + Optional: true, + Sensitive: false, + ForceNew: true, + Description: "Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using tunnel_config, tunnel_route or tunnel_virtual_network resources.", + }, "cname": { Type: schema.TypeString, Computed: true, From 36b6b2c17c3d2ed9d715c250698537be826e09eb Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 12 Apr 2023 13:23:41 +1000 Subject: [PATCH 2/3] `make docs` --- docs/resources/tunnel.md | 5 ++++- .../resource_cloudflare_tunnel_test.go | 2 ++ internal/sdkv2provider/schema_cloudflare_tunnel.go | 13 ++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/resources/tunnel.md b/docs/resources/tunnel.md index 90b9014d55..f0623cb00c 100644 --- a/docs/resources/tunnel.md +++ b/docs/resources/tunnel.md @@ -30,7 +30,10 @@ resource "cloudflare_tunnel" "example" { - `account_id` (String) The account identifier to target for the resource. **Modifying this attribute will force creation of a new resource.** - `name` (String) A user-friendly name chosen when the tunnel is created. **Modifying this attribute will force creation of a new resource.** - `secret` (String, Sensitive) 32 or more bytes, encoded as a base64 string. The Create Argo Tunnel endpoint sets this as the tunnel's password. Anyone wishing to run the tunnel needs this password. **Modifying this attribute will force creation of a new resource.** -- `config_src` (String, Optional) Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using `tunnel_config`, `tunnel_route` or `tunnel_virtual_network` resources. **Modifying this attribute will force creation of a new resource.** + +### Optional + +- `config_src` (String) Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using tunnel_config, tunnel_route or tunnel_virtual_network resources. Available values: `local`, `cloudflare`. **Modifying this attribute will force creation of a new resource.** ### Read-Only diff --git a/internal/sdkv2provider/resource_cloudflare_tunnel_test.go b/internal/sdkv2provider/resource_cloudflare_tunnel_test.go index 3ca9d8d2fe..c6a8577623 100644 --- a/internal/sdkv2provider/resource_cloudflare_tunnel_test.go +++ b/internal/sdkv2provider/resource_cloudflare_tunnel_test.go @@ -76,6 +76,7 @@ func TestAccCloudflareTunnelCreate_Managed(t *testing.T) { resource.TestCheckResourceAttr(name, "name", rnd), resource.TestCheckResourceAttr(name, "secret", "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg="), resource.TestMatchResourceAttr(name, "cname", regexp.MustCompile(".*\\.cfargotunnel\\.com")), + resource.TestCheckResourceAttr(name, "config_src", "cloudflare"), ), }, }, @@ -116,6 +117,7 @@ func TestAccCloudflareTunnelCreate_Unmanaged(t *testing.T) { resource.TestCheckResourceAttr(name, "name", rnd), resource.TestCheckResourceAttr(name, "secret", "AQIDBAUGBwgBAgMEBQYHCAECAwQFBgcIAQIDBAUGBwg="), resource.TestMatchResourceAttr(name, "cname", regexp.MustCompile(".*\\.cfargotunnel\\.com")), + resource.TestCheckResourceAttr(name, "config_src", "local"), ), }, }, diff --git a/internal/sdkv2provider/schema_cloudflare_tunnel.go b/internal/sdkv2provider/schema_cloudflare_tunnel.go index 9df0dd514c..1a33f25d7c 100644 --- a/internal/sdkv2provider/schema_cloudflare_tunnel.go +++ b/internal/sdkv2provider/schema_cloudflare_tunnel.go @@ -1,8 +1,11 @@ package sdkv2provider import ( + "fmt" + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func resourceCloudflareTunnelSchema() map[string]*schema.Schema { @@ -27,11 +30,11 @@ func resourceCloudflareTunnelSchema() map[string]*schema.Schema { Description: "32 or more bytes, encoded as a base64 string. The Create Argo Tunnel endpoint sets this as the tunnel's password. Anyone wishing to run the tunnel needs this password.", }, "config_src": { - Type: schema.TypeString, - Optional: true, - Sensitive: false, - ForceNew: true, - Description: "Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using tunnel_config, tunnel_route or tunnel_virtual_network resources.", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"local", "cloudflare"}, false), + Description: fmt.Sprintf("Indicates if this is a locally or remotely configured tunnel. If `local`, manage the tunnel using a YAML file on the origin machine. If `cloudflare`, manage the tunnel on the Zero Trust dashboard or using tunnel_config, tunnel_route or tunnel_virtual_network resources. %s", renderAvailableDocumentationValuesStringSlice([]string{"local", "cloudflare"})), }, "cname": { Type: schema.TypeString, From 8fbcdec4b108d493460674d0b4bbe4d862ba3ac1 Mon Sep 17 00:00:00 2001 From: Jacob Bednarz Date: Wed, 12 Apr 2023 13:41:34 +1000 Subject: [PATCH 3/3] save `config_src` to state when it is present in the response --- internal/sdkv2provider/resource_cloudflare_tunnel.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/sdkv2provider/resource_cloudflare_tunnel.go b/internal/sdkv2provider/resource_cloudflare_tunnel.go index de20ac71b7..ee8bb28625 100644 --- a/internal/sdkv2provider/resource_cloudflare_tunnel.go +++ b/internal/sdkv2provider/resource_cloudflare_tunnel.go @@ -70,6 +70,14 @@ func resourceCloudflareTunnelRead(ctx context.Context, d *schema.ResourceData, m d.Set("cname", fmt.Sprintf("%s.%s", tunnel.ID, argoTunnelCNAME)) d.Set("tunnel_token", token) + if d.Get("config_src").(string) != "" { + if tunnel.RemoteConfig { + d.Set("config_src", "cloudflare") + } else { + d.Set("config_src", "local") + } + } + return nil }