Skip to content

Commit

Permalink
FLPROD-796: Register snippet and snippet_rules resources
Browse files Browse the repository at this point in the history
  • Loading branch information
Denis Davydov committed Nov 23, 2024
1 parent e2013d8 commit 276bc81
Show file tree
Hide file tree
Showing 10 changed files with 728 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/4565.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
cloudflare_snippet_rules and cloudflare_snippet
```
4 changes: 4 additions & 0 deletions internal/framework/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/r2_bucket"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/risk_behavior"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/rulesets"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/snippet_rules"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/snippets"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/turnstile"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/user"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/workers_for_platforms_dispatch_namespace"
Expand Down Expand Up @@ -368,6 +370,8 @@ func (p *CloudflareProvider) Configure(ctx context.Context, req provider.Configu

func (p *CloudflareProvider) Resources(ctx context.Context) []func() resource.Resource {
return []func() resource.Resource{
snippet_rules.NewResource,
snippets.NewResource,
cloud_connector_rules.NewResource,
d1.NewResource,
email_routing_address.NewResource,
Expand Down
15 changes: 15 additions & 0 deletions internal/framework/service/snippet_rules/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package snippet_rules

import "github.com/hashicorp/terraform-plugin-framework/types"

type SnippetRules struct {
ZoneID types.String `tfsdk:"zone_id"`
Rules []SnippetRule `tfsdk:"rules"`
}

type SnippetRule struct {
Enabled types.Bool `tfsdk:"enabled"`
Expression types.String `tfsdk:"expression"`
Description types.String `tfsdk:"description"`
SnippetName types.String `tfsdk:"snippet_name"`
}
166 changes: 166 additions & 0 deletions internal/framework/service/snippet_rules/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package snippet_rules

import (
"context"
"fmt"
"strings"

cfv1 "github.com/cloudflare/cloudflare-go"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/muxclient"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/types"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ resource.Resource = &SnippetRulesResource{}
var _ resource.ResourceWithImportState = &SnippetRulesResource{}

// SnippetRulesResource defines the resource implementation.
type SnippetRulesResource struct {
client *muxclient.Client
}

func NewResource() resource.Resource {
return &SnippetRulesResource{}
}

func (r *SnippetRulesResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_snippet_rules"
}

func (r *SnippetRulesResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*muxclient.Client)

if !ok {
resp.Diagnostics.AddError(
"unexpected resource configure type",
fmt.Sprintf("Expected *muxclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

r.client = client
}

func resourceFromAPIResponse(data *SnippetRules, rules []cfv1.SnippetRule) {
result := make([]SnippetRule, len(rules))
for i, rule := range rules {
result[i].Description = types.StringValue(rule.Description)
result[i].Expression = types.StringValue(rule.Expression)
result[i].Enabled = types.BoolPointerValue(rule.Enabled)
result[i].SnippetName = types.StringValue(rule.SnippetName)
}
data.Rules = result
}

func requestFromResource(data *SnippetRules) []cfv1.SnippetRule {
rules := make([]cfv1.SnippetRule, len(data.Rules))
for i, rule := range data.Rules {
rules[i] = cfv1.SnippetRule{
Enabled: rule.Enabled.ValueBoolPointer(),
Expression: rule.Expression.ValueString(),
SnippetName: rule.SnippetName.ValueString(),
Description: rule.Description.ValueString(),
}
}
return rules
}

func (r *SnippetRulesResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data *SnippetRules

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

rules, err := r.client.V1.UpdateZoneSnippetsRules(ctx, cfv1.ZoneIdentifier(data.ZoneID.ValueString()),
requestFromResource(data),
)
if err != nil {
resp.Diagnostics.AddError("failed to create snippet rules", err.Error())
return
}

resourceFromAPIResponse(data, rules)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *SnippetRulesResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data *SnippetRules

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

rules, err := r.client.V1.ListZoneSnippetsRules(ctx, cfv1.ZoneIdentifier(data.ZoneID.ValueString()))
if err != nil {
resp.Diagnostics.AddError("failed reading snippet rules", err.Error())
return
}
resourceFromAPIResponse(data, rules)
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *SnippetRulesResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data *SnippetRules

resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

rules, err := r.client.V1.UpdateZoneSnippetsRules(ctx, cfv1.ZoneIdentifier(data.ZoneID.ValueString()),
requestFromResource(data),
)
if err != nil {
resp.Diagnostics.AddError("failed to create snippet rules", err.Error())
return
}

resourceFromAPIResponse(data, rules)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *SnippetRulesResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data *SnippetRules

resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

_, err := r.client.V1.UpdateZoneSnippetsRules(ctx, cfv1.ZoneIdentifier(data.ZoneID.ValueString()), []cfv1.SnippetRule{})

if err != nil {
resp.Diagnostics.AddError("failed to delete snippet rules", err.Error())
return
}
}

func (r *SnippetRulesResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
idparts := strings.Split(req.ID, "/")
if len(idparts) != 2 {
resp.Diagnostics.AddError("error importing snippet rule", `invalid ID specified. Please specify the ID as "<zone_id>/<snippet_rule_id>"`)
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(
ctx, path.Root("zone_id"), idparts[0],
)...)
resp.Diagnostics.Append(resp.State.SetAttribute(
ctx, path.Root("id"), idparts[1],
)...)
}
151 changes: 151 additions & 0 deletions internal/framework/service/snippet_rules/resource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package snippet_rules_test

import (
"context"
"fmt"
"os"
"testing"

cloudflare "github.com/cloudflare/cloudflare-go"
"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest"
"github.com/cloudflare/terraform-provider-cloudflare/internal/consts"
"github.com/cloudflare/terraform-provider-cloudflare/internal/utils"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/pkg/errors"
)

func TestMain(m *testing.M) {
resource.TestMain(m)
}

func init() {
resource.AddTestSweepers("cloudflare_snippet_rules", &resource.Sweeper{
Name: "cloudflare_snippet_rules",
F: testSweepCloudflareSnippetRules,
})
}

func testSweepCloudflareSnippetRules(r string) error {
ctx := context.Background()
client, clientErr := acctest.SharedV1Client()
if clientErr != nil {
tflog.Error(ctx, fmt.Sprintf("Failed to create Cloudflare client: %s", clientErr))
}

zone := os.Getenv("CLOUDFLARE_ZONE_ID")
if zone == "" {
return errors.New("CLOUDFLARE_ZONE_ID must be set")
}

_, err := client.UpdateZoneSnippetsRules(context.Background(), cloudflare.ZoneIdentifier(zone), []cloudflare.SnippetRule{})
if err != nil {
tflog.Error(ctx, fmt.Sprintf("Failed to disable Cloudflare Zone Snippet Rules: %s", err))
}

return nil
}

func TestAccCloudflareSnippetRules(t *testing.T) {
t.Parallel()

rnd := utils.GenerateRandomResourceName()
zoneID := os.Getenv("CLOUDFLARE_ZONE_ID")
resourceName := "cloudflare_snippet_rules." + rnd
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.TestAccPreCheck(t) },
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccCheckCloudflareSnippetRules(rnd, zoneID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(resourceName, "rules.#", "3"),
resource.TestCheckResourceAttr(resourceName, "rules.0.%", "4"),
resource.TestCheckResourceAttr(resourceName, "rules.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.0.expression", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.0.description", "some description 1"),
resource.TestCheckResourceAttr(resourceName, "rules.0.snippet_name", "test_snippet_1"),

resource.TestCheckResourceAttr(resourceName, "rules.1.%", "4"),
resource.TestCheckResourceAttr(resourceName, "rules.1.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.1.expression", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.1.description", "some description 2"),
resource.TestCheckResourceAttr(resourceName, "rules.1.snippet_name", "test_snippet2"),

resource.TestCheckResourceAttr(resourceName, "rules.2.%", "4"),
resource.TestCheckResourceAttr(resourceName, "rules.2.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.2.expression", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.2.description", "some description 3"),
resource.TestCheckResourceAttr(resourceName, "rules.2.snippet_name", "test_snippet_3"),
),
},
{
Config: testAccCheckCloudflareSnippetRulesRemovedRule(rnd, zoneID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, consts.ZoneIDSchemaKey, zoneID),
resource.TestCheckResourceAttr(resourceName, "rules.#", "2"),

resource.TestCheckResourceAttr(resourceName, "rules.0.%", "4"),
resource.TestCheckResourceAttr(resourceName, "rules.0.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.0.expression", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.0.description", "some description 2"),
resource.TestCheckResourceAttr(resourceName, "rules.0.snippet_name", "test_snippet_2"),

resource.TestCheckResourceAttr(resourceName, "rules.1.%", "4"),
resource.TestCheckResourceAttr(resourceName, "rules.1.enabled", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.1.expression", "true"),
resource.TestCheckResourceAttr(resourceName, "rules.1.description", "some description 3"),
resource.TestCheckResourceAttr(resourceName, "rules.1.snippet_name", "test_snippet_3"),
),
},
},
})
}

func testAccCheckCloudflareSnippetRules(rnd, zoneID string) string {
return fmt.Sprintf(`
resource "cloudflare_snippet_rules" "%[1]s" {
zone_id = "%[2]s"
rules {
enabled = true
expression = "true"
description = "some description 1"
snippet_name = "test_snippet_1"
}
rules {
enabled = true
expression = "true"
description = "some description 2"
snippet_name = "test_snippet_2"
}
rules {
enabled = true
expression = "true"
description = "some description 3"
snippet_name = "test_snippet_3"
}
}`, rnd, zoneID)
}

func testAccCheckCloudflareSnippetRulesRemovedRule(rnd, zoneID string) string {
return fmt.Sprintf(`
resource "cloudflare_snippet_rules" "%[1]s" {
zone_id = "%[2]s"
rules {
enabled = true
expression = "true"
description = "some description 2"
snippet_name = "test_snippet_2"
}
rules {
enabled = true
expression = "true"
description = "some description 3"
snippet_name = "test_snippet_3"
}
}`, rnd, zoneID)
}
Loading

0 comments on commit 276bc81

Please sign in to comment.