From 70f731ac6a9c9c17ce9aac76d22628a203291333 Mon Sep 17 00:00:00 2001 From: Michael Borkenstein Date: Wed, 2 Mar 2022 13:35:06 -0600 Subject: [PATCH] GATE-2448: Adds support for proxy endpoints --- CHANGELOG.md | 1 + cloudflare/provider.go | 1 + ...source_cloudflare_teams_proxy_endpoints.go | 132 ++++++++++++++++++ ...e_cloudflare_teams_proxy_endpoints_test.go | 74 ++++++++++ ...schema_cloudflare_teams_proxy_endpoints.go | 26 ++++ 5 files changed, 234 insertions(+) create mode 100644 cloudflare/resource_cloudflare_teams_proxy_endpoints.go create mode 100644 cloudflare/resource_cloudflare_teams_proxy_endpoints_test.go create mode 100644 cloudflare/schema_cloudflare_teams_proxy_endpoints.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 19eb52b1c8e..18fb0280672 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * **New Resource:** `cloudflare_waiting_room_event` ([#1509](https://github.com/cloudflare/terraform-provider-cloudflare/issues/1509)) +* **New Resource:** `cloudflare_teams_proxy_endpoint` ENHANCEMENTS: diff --git a/cloudflare/provider.go b/cloudflare/provider.go index 57fdb0b4de2..5899284c9f9 100644 --- a/cloudflare/provider.go +++ b/cloudflare/provider.go @@ -173,6 +173,7 @@ func Provider() *schema.Provider { "cloudflare_teams_list": resourceCloudflareTeamsList(), "cloudflare_teams_location": resourceCloudflareTeamsLocation(), "cloudflare_teams_rule": resourceCloudflareTeamsRule(), + "cloudflare_teams_proxy_endpoint": resourceCloudflareTeamsProxyEndpoint(), "cloudflare_waf_group": resourceCloudflareWAFGroup(), "cloudflare_waf_override": resourceCloudflareWAFOverride(), "cloudflare_waf_package": resourceCloudflareWAFPackage(), diff --git a/cloudflare/resource_cloudflare_teams_proxy_endpoints.go b/cloudflare/resource_cloudflare_teams_proxy_endpoints.go new file mode 100644 index 00000000000..7d13f6e44c7 --- /dev/null +++ b/cloudflare/resource_cloudflare_teams_proxy_endpoints.go @@ -0,0 +1,132 @@ +package cloudflare + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/cloudflare/cloudflare-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceCloudflareTeamsProxyEndpoint() *schema.Resource { + return &schema.Resource{ + Schema: resourceCloudflareTeamsProxyEndpointSchema(), + Create: resourceCloudflareTeamsProxyEndpointCreate, + Read: resourceCloudflareTeamsProxyEndpointRead, + Update: resourceCloudflareTeamsProxyEndpointUpdate, + Delete: resourceCloudflareTeamsProxyEndpointDelete, + Importer: &schema.ResourceImporter{ + State: resourceCloudflareTeamsProxyEndpointImport, + }, + } +} + +func resourceCloudflareTeamsProxyEndpointRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + accountID := d.Get("account_id").(string) + + endpoint, err := client.TeamsProxyEndpoint(context.Background(), accountID, d.Id()) + if err != nil { + if strings.Contains(err.Error(), "HTTP status 400") { + log.Printf("[INFO] Teams Proxy Endpoint %s no longer exists", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("error finding Teams Proxy Endpoint %q: %s", d.Id(), err) + } + + if err := d.Set("name", endpoint.Name); err != nil { + return fmt.Errorf("error parsing Proxy Endpoint name") + } + if err := d.Set("ips", endpoint.IPs); err != nil { + return fmt.Errorf("error parsing Proxy Endpoint IPs") + } + if err := d.Set("subdomain", endpoint.Subdomain); err != nil { + return fmt.Errorf("error parsing Proxy Endpoint subdomain") + } + return nil +} +func resourceCloudflareTeamsProxyEndpointCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + + accountID := d.Get("account_id").(string) + newProxyEndpoint := cloudflare.TeamsProxyEndpoint{ + Name: d.Get("name").(string), + IPs: inflateProxyEndpointIPs(d.Get("ips").([]interface{})), + } + + log.Printf("[DEBUG] Creating Cloudflare Teams Proxy Endpoint from struct: %+v", newProxyEndpoint) + + proxyEndpoint, err := client.CreateTeamsProxyEndpoint(context.Background(), accountID, newProxyEndpoint) + if err != nil { + return fmt.Errorf("error creating Teams Proxy Endpoint for account %q: %s", accountID, err) + } + + d.SetId(proxyEndpoint.ID) + return resourceCloudflareTeamsProxyEndpointRead(d, meta) + +} +func resourceCloudflareTeamsProxyEndpointUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + accountID := d.Get("account_id").(string) + updatedProxyEndpoint := cloudflare.TeamsProxyEndpoint{ + ID: d.Id(), + Name: d.Get("name").(string), + IPs: inflateProxyEndpointIPs(d.Get("ips").([]interface{})), + } + log.Printf("[DEBUG] Updating Cloudflare Teams Proxy Endpoint from struct: %+v", updatedProxyEndpoint) + + teamsProxyEndpoint, err := client.UpdateTeamsProxyEndpoint(context.Background(), accountID, updatedProxyEndpoint) + if err != nil { + return fmt.Errorf("error updating Teams Proxy Endpoint for account %q: %s", accountID, err) + } + if teamsProxyEndpoint.ID == "" { + return fmt.Errorf("failed to find Teams Proxy Endpoint ID in update response; resource was empty") + } + return resourceCloudflareTeamsProxyEndpointRead(d, meta) +} + +func resourceCloudflareTeamsProxyEndpointDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*cloudflare.API) + id := d.Id() + accountID := d.Get("account_id").(string) + + log.Printf("[DEBUG] Deleting Cloudflare Teams Proxy Endpoint using ID: %s", id) + + err := client.DeleteTeamsProxyEndpoint(context.Background(), accountID, id) + if err != nil { + return fmt.Errorf("error deleting Teams Proxy Endpoint for account %q: %s", accountID, err) + } + + return resourceCloudflareTeamsProxyEndpointRead(d, meta) +} + +func resourceCloudflareTeamsProxyEndpointImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + attributes := strings.SplitN(d.Id(), "/", 2) + + if len(attributes) != 2 { + return nil, fmt.Errorf("invalid id (\"%s\") specified, should be in format \"accountID/teamsProxyEndpointID\"", d.Id()) + } + + accountID, teamsProxyEndpointID := attributes[0], attributes[1] + + log.Printf("[DEBUG] Importing Cloudflare Teams Proxy Endpoint: id %s for account %s", teamsProxyEndpointID, accountID) + + d.Set("account_id", accountID) + d.SetId(teamsProxyEndpointID) + + err := resourceCloudflareTeamsProxyEndpointRead(d, meta) + + return []*schema.ResourceData{d}, err + +} + +func inflateProxyEndpointIPs(ips []interface{}) []string { + i := make([]string, len(ips)) + for x, ip := range ips { + i[x] = ip.(string) + } + return i +} diff --git a/cloudflare/resource_cloudflare_teams_proxy_endpoints_test.go b/cloudflare/resource_cloudflare_teams_proxy_endpoints_test.go new file mode 100644 index 00000000000..3b06b7ff494 --- /dev/null +++ b/cloudflare/resource_cloudflare_teams_proxy_endpoints_test.go @@ -0,0 +1,74 @@ +package cloudflare + +import ( + "context" + "fmt" + "os" + "regexp" + "testing" + + "github.com/cloudflare/cloudflare-go" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCloudflareTeamsProxyEndpointBasic(t *testing.T) { + // Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the Access + // service does not yet support the API tokens and it results in + // misleading state error messages. + if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { + defer func(apiToken string) { + os.Setenv("CLOUDFLARE_API_TOKEN", apiToken) + }(os.Getenv("CLOUDFLARE_API_TOKEN")) + os.Setenv("CLOUDFLARE_API_TOKEN", "") + } + + rnd := generateRandomResourceName() + name := fmt.Sprintf("cloudflare_teams_proxy_endpoint.%s", rnd) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccessAccPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCloudflareTeamsProxyEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudflareTeamsProxyEndpointConfigBasic(rnd, accountID), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "account_id", accountID), + resource.TestCheckResourceAttr(name, "name", rnd), + resource.TestCheckResourceAttr(name, "ips.0", "1.2.3.4/32"), + resource.TestMatchResourceAttr(name, "subdomain", regexp.MustCompile("^[a-zA-Z0-9]+$")), + ), + }, + }, + }) +} + +func testAccCloudflareTeamsProxyEndpointConfigBasic(rnd, accountID string) string { + return fmt.Sprintf(` +resource "cloudflare_teams_proxy_endpoint" "%[1]s" { + name = "%[1]s" + account_id = "%[2]s" + ips = ["1.2.3.4/32"] +} +`, rnd, accountID) +} + +func testAccCheckCloudflareTeamsProxyEndpointDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudflare.API) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudflare_teams_proxy_endpoint" { + continue + } + + _, err := client.TeamsProxyEndpoint(context.Background(), rs.Primary.Attributes["account_id"], rs.Primary.ID) + if err == nil { + return fmt.Errorf("teams Proxy Endpoint still exists") + } + } + + return nil +} diff --git a/cloudflare/schema_cloudflare_teams_proxy_endpoints.go b/cloudflare/schema_cloudflare_teams_proxy_endpoints.go new file mode 100644 index 00000000000..0e0021a94ec --- /dev/null +++ b/cloudflare/schema_cloudflare_teams_proxy_endpoints.go @@ -0,0 +1,26 @@ +package cloudflare + +import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + +func resourceCloudflareTeamsProxyEndpointSchema() map[string]*schema.Schema { + + return map[string]*schema.Schema{ + "account_id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "subdomain": { + Type: schema.TypeString, + Computed: true, + }, + "ips": { + Type: schema.TypeList, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + }, + } +}