-
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 #2760 from djhworld/dharper/APISHI-2354
resource/cloudflare_api_shield_operation: add API Shield Operation resource
- Loading branch information
Showing
7 changed files
with
313 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_api_shield_operation | ||
``` |
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,36 @@ | ||
--- | ||
page_title: "cloudflare_api_shield_operation Resource - Cloudflare" | ||
subcategory: "" | ||
description: |- | ||
Provides a resource to manage an operation in API Shield Endpoint Management. | ||
--- | ||
|
||
# cloudflare_api_shield_operation (Resource) | ||
|
||
Provides a resource to manage an operation in API Shield Endpoint Management. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "cloudflare_api_shield_operation" "example" { | ||
zone_id = "0da42c8d2132a9ddaf714f9e7c920711" | ||
method = "GET" | ||
host = "api.example.com" | ||
endpoint = "/path" | ||
} | ||
``` | ||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `endpoint` (String) The endpoint which can contain path parameter templates in curly braces, each will be replaced from left to right with `{varN}`, starting with `{var1}`. This will then be [Cloudflare-normalized](https://developers.cloudflare.com/rules/normalization/how-it-works/). **Modifying this attribute will force creation of a new resource.** | ||
- `host` (String) RFC3986-compliant host. **Modifying this attribute will force creation of a new resource.** | ||
- `method` (String) The HTTP method used to access the endpoint. **Modifying this attribute will force creation of a new resource.** | ||
- `zone_id` (String) The zone identifier to target for the resource. **Modifying this attribute will force creation of a new resource.** | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
|
6 changes: 6 additions & 0 deletions
6
examples/resources/cloudflare_api_shield_operation/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,6 @@ | ||
resource "cloudflare_api_shield_operation" "example" { | ||
zone_id = "0da42c8d2132a9ddaf714f9e7c920711" | ||
method = "GET" | ||
host = "api.example.com" | ||
endpoint = "/path" | ||
} |
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
108 changes: 108 additions & 0 deletions
108
internal/sdkv2provider/resource_cloudflare_api_shield_operation.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,108 @@ | ||
package sdkv2provider | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/pkg/errors" | ||
|
||
"github.com/MakeNowJust/heredoc/v2" | ||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceCloudflareAPIShieldOperation() *schema.Resource { | ||
return &schema.Resource{ | ||
Schema: resourceCloudflareAPIShieldOperationSchema(), | ||
CreateContext: resourceCloudflareAPIShieldOperationCreate, | ||
ReadContext: resourceCloudflareAPIShieldOperationRead, | ||
DeleteContext: resourceCloudflareAPIShieldOperationDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: nil, | ||
}, | ||
Description: heredoc.Doc(` | ||
Provides a resource to manage an operation in API Shield Endpoint Management. | ||
`), | ||
} | ||
} | ||
|
||
func resourceCloudflareAPIShieldOperationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
zoneID := d.Get(consts.ZoneIDSchemaKey).(string) | ||
|
||
ops, err := client.CreateAPIShieldOperations( | ||
ctx, | ||
cloudflare.ZoneIdentifier(zoneID), | ||
cloudflare.CreateAPIShieldOperationsParams{ | ||
Operations: []cloudflare.APIShieldBasicOperation{ | ||
{ | ||
Method: d.Get("method").(string), | ||
Host: d.Get("host").(string), | ||
Endpoint: d.Get("endpoint").(string), | ||
}, | ||
}, | ||
}, | ||
) | ||
|
||
if err != nil { | ||
return diag.FromErr(errors.Wrap(err, "failed to create API Shield Operation")) | ||
} | ||
|
||
if length := len(ops); length != 1 { | ||
return diag.FromErr(fmt.Errorf("expected output to have 1 entry but got: %d", length)) | ||
} | ||
|
||
d.SetId(ops[0].ID) | ||
return resourceCloudflareAPIShieldOperationRead(ctx, d, meta) | ||
} | ||
|
||
func resourceCloudflareAPIShieldOperationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
zoneID := d.Get(consts.ZoneIDSchemaKey).(string) | ||
|
||
op, err := client.GetAPIShieldOperation( | ||
ctx, | ||
cloudflare.ZoneIdentifier(zoneID), | ||
cloudflare.GetAPIShieldOperationParams{ | ||
OperationID: d.Id(), | ||
}, | ||
) | ||
|
||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("failed to fetch API Shield Operation: %w", err)) | ||
} | ||
|
||
if err := d.Set("method", op.Method); err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
if err := d.Set("host", op.Host); err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
if err := d.Set("endpoint", op.Endpoint); err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
d.SetId(op.ID) | ||
return nil | ||
} | ||
|
||
func resourceCloudflareAPIShieldOperationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*cloudflare.API) | ||
zoneID := d.Get(consts.ZoneIDSchemaKey).(string) | ||
|
||
err := client.DeleteAPIShieldOperation( | ||
ctx, | ||
cloudflare.ZoneIdentifier(zoneID), | ||
cloudflare.DeleteAPIShieldOperationParams{ | ||
OperationID: d.Id(), | ||
}, | ||
) | ||
if err != nil { | ||
return diag.FromErr(fmt.Errorf("failed to fetch API Shield Operation: %w", err)) | ||
} | ||
|
||
return nil | ||
} |
122 changes: 122 additions & 0 deletions
122
internal/sdkv2provider/resource_cloudflare_api_shield_operation_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,122 @@ | ||
package sdkv2provider | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"testing" | ||
|
||
"github.com/cloudflare/cloudflare-go" | ||
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts" | ||
"github.com/hashicorp/terraform-plugin-testing/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-testing/terraform" | ||
) | ||
|
||
func TestAccCloudflareAPIShieldOperation_Create(t *testing.T) { | ||
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the API token | ||
// endpoint does not yet support the API tokens without an explicit scope. | ||
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { | ||
t.Setenv("CLOUDFLARE_API_TOKEN", "") | ||
} | ||
|
||
rnd := generateRandomResourceName() | ||
resourceID := "cloudflare_api_shield_operation." + rnd | ||
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") | ||
domain := os.Getenv("CLOUDFLARE_DOMAIN") | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProviderFactories: providerFactories, | ||
CheckDestroy: testAccCheckAPIShieldOperationDelete, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareAPIShieldOperation(rnd, zoneID, cloudflare.APIShieldBasicOperation{Method: "GET", Host: domain, Endpoint: "/example/path"}), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), | ||
resource.TestCheckResourceAttr(resourceID, "method", "GET"), | ||
resource.TestCheckResourceAttr(resourceID, "host", domain), | ||
resource.TestCheckResourceAttr(resourceID, "endpoint", "/example/path"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func TestAccCloudflareAPIShieldOperation_ForceNew(t *testing.T) { | ||
// Temporarily unset CLOUDFLARE_API_TOKEN if it is set as the API token | ||
// endpoint does not yet support the API tokens without an explicit scope. | ||
if os.Getenv("CLOUDFLARE_API_TOKEN") != "" { | ||
t.Setenv("CLOUDFLARE_API_TOKEN", "") | ||
} | ||
|
||
rnd := generateRandomResourceName() | ||
resourceID := "cloudflare_api_shield_operation." + rnd | ||
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") | ||
domain := os.Getenv("CLOUDFLARE_DOMAIN") | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
ProviderFactories: providerFactories, | ||
CheckDestroy: testAccCheckAPIShieldOperationDelete, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccCloudflareAPIShieldOperation(rnd, zoneID, cloudflare.APIShieldBasicOperation{Method: "GET", Host: domain, Endpoint: "/example/path"}), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), | ||
resource.TestCheckResourceAttr(resourceID, "method", "GET"), | ||
resource.TestCheckResourceAttr(resourceID, "host", domain), | ||
resource.TestCheckResourceAttr(resourceID, "endpoint", "/example/path"), | ||
), | ||
}, | ||
{ | ||
Config: testAccCloudflareAPIShieldOperation(rnd, zoneID, cloudflare.APIShieldBasicOperation{Method: "POST", Host: domain, Endpoint: "/example/path"}), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr(resourceID, consts.ZoneIDSchemaKey, zoneID), | ||
resource.TestCheckResourceAttr(resourceID, "method", "POST"), // check that we've 'updated' the value | ||
resource.TestCheckResourceAttr(resourceID, "host", domain), | ||
resource.TestCheckResourceAttr(resourceID, "endpoint", "/example/path"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAPIShieldOperationDelete(s *terraform.State) error { | ||
client := testAccProvider.Meta().(*cloudflare.API) | ||
|
||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "cloudflare_api_shield_operation" { | ||
continue | ||
} | ||
|
||
_, err := client.GetAPIShieldOperation( | ||
context.Background(), | ||
cloudflare.ZoneIdentifier(rs.Primary.Attributes[consts.ZoneIDSchemaKey]), | ||
cloudflare.GetAPIShieldOperationParams{ | ||
OperationID: rs.Primary.Attributes["id"], | ||
}, | ||
) | ||
if err == nil { | ||
return fmt.Errorf("operation still exists") | ||
} | ||
|
||
var notFoundError *cloudflare.NotFoundError | ||
if !errors.As(err, ¬FoundError) { | ||
return fmt.Errorf("expected not found error but got: %w", err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func testAccCloudflareAPIShieldOperation(resourceName, zone string, op cloudflare.APIShieldBasicOperation) string { | ||
return fmt.Sprintf(` | ||
resource "cloudflare_api_shield_operation" "%[1]s" { | ||
zone_id = "%[2]s" | ||
method = "%[3]s" | ||
host = "%[4]s" | ||
endpoint = "%[5]s" | ||
} | ||
`, resourceName, zone, op.Method, op.Host, op.Endpoint) | ||
} |
37 changes: 37 additions & 0 deletions
37
internal/sdkv2provider/schema_cloudflare_api_shield_operation.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,37 @@ | ||
package sdkv2provider | ||
|
||
import ( | ||
"github.com/MakeNowJust/heredoc/v2" | ||
|
||
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func resourceCloudflareAPIShieldOperationSchema() map[string]*schema.Schema { | ||
return map[string]*schema.Schema{ | ||
consts.ZoneIDSchemaKey: { | ||
Description: consts.ZoneIDSchemaDescription, | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"method": { | ||
Description: "The HTTP method used to access the endpoint", | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"host": { | ||
Description: "RFC3986-compliant host", | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"endpoint": { | ||
Description: heredoc.Doc("The endpoint which can contain path parameter templates in curly braces, each will be replaced from left to right with `{varN}`, starting with `{var1}`. This will then be [Cloudflare-normalized](https://developers.cloudflare.com/rules/normalization/how-it-works/)"), | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
} | ||
} |