From 0f6fb3eebd6e9eef036dd55f9dd7915dc3a36d96 Mon Sep 17 00:00:00 2001 From: Michele Russo Date: Thu, 5 Dec 2024 16:54:57 +0100 Subject: [PATCH] Add content scanning resource --- .../service/content_scanning/model.go | 8 + .../service/content_scanning/resource.go | 153 ++++++++++++++++++ .../service/content_scanning/resource_test.go | 89 ++++++++++ .../service/content_scanning/schema.go | 25 +++ 4 files changed, 275 insertions(+) create mode 100644 internal/framework/service/content_scanning/model.go create mode 100644 internal/framework/service/content_scanning/resource.go create mode 100644 internal/framework/service/content_scanning/resource_test.go create mode 100644 internal/framework/service/content_scanning/schema.go diff --git a/internal/framework/service/content_scanning/model.go b/internal/framework/service/content_scanning/model.go new file mode 100644 index 0000000000..776c0efb53 --- /dev/null +++ b/internal/framework/service/content_scanning/model.go @@ -0,0 +1,8 @@ +package content_scanning + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type ContentScanningModel struct { + ZoneID types.String `tfsdk:"zone_id"` + Enabled types.Bool `tfsdk:"enabled"` +} diff --git a/internal/framework/service/content_scanning/resource.go b/internal/framework/service/content_scanning/resource.go new file mode 100644 index 0000000000..bffc989e4b --- /dev/null +++ b/internal/framework/service/content_scanning/resource.go @@ -0,0 +1,153 @@ +package content_scanning + +import ( + "context" + "fmt" + + "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" +) + +var ( + _ resource.Resource = &ContentScanningResource{} +) + +func NewResource() resource.Resource { + return &ContentScanningResource{} +} + +type ContentScanningResource struct { + client *muxclient.Client +} + +func (r *ContentScanningResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_content_scanning" +} + +func (r *ContentScanningResource) Configure(_ 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 (r *ContentScanningResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan ContentScanningModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + zoneID := cloudflare.ZoneIdentifier(plan.ZoneID.ValueString()) + if plan.Enabled.ValueBool() { + params := cloudflare.ContentScanningEnableParams{} + _, err := r.client.V1.ContentScanningEnable(ctx, zoneID, params) + if err != nil { + resp.Diagnostics.AddError("Error enabling (Create) Content Scanning", err.Error()) + return + } + } else { + params := cloudflare.ContentScanningDisableParams{} + _, err := r.client.V1.ContentScanningDisable(ctx, zoneID, params) + if err != nil { + resp.Diagnostics.AddError("Error disabling (Create) Content Scanning", err.Error()) + return + } + } + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) +} + +func (r *ContentScanningResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state ContentScanningModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + zoneID := state.ZoneID.ValueString() + status, err := r.client.V1.ContentScanningStatus(ctx, cloudflare.ZoneIdentifier(zoneID), cloudflare.ContentScanningStatusParams{}) + if err != nil { + resp.Diagnostics.AddError("Error reading Content Scanning status", err.Error()) + return + } + switch status.Result.Value { + case "enabled": + state.Enabled = types.BoolValue(true) + case "disabled": + state.Enabled = types.BoolValue(false) + default: + resp.Diagnostics.AddError("Unrecognized state", fmt.Sprintf("Unrecognized state = %s for Content Scanning", status.Result.Value)) + return + } + + diags = resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) +} + +func (r *ContentScanningResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan ContentScanningModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + zoneID := cloudflare.ZoneIdentifier(plan.ZoneID.ValueString()) + if plan.Enabled.ValueBool() { + params := cloudflare.ContentScanningEnableParams{} + _, err := r.client.V1.ContentScanningEnable(ctx, zoneID, params) + if err != nil { + resp.Diagnostics.AddError("Error enabling (Update) Content Scanning", err.Error()) + return + } + } else { + params := cloudflare.ContentScanningDisableParams{} + _, err := r.client.V1.ContentScanningDisable(ctx, zoneID, params) + if err != nil { + resp.Diagnostics.AddError("Error disabling (Update) Content Scanning", err.Error()) + return + } + } + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) +} + +// Delete disables the Content Scanning feature +func (r *ContentScanningResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ContentScanningModel + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + zoneID := cloudflare.ZoneIdentifier(state.ZoneID.ValueString()) + params := cloudflare.ContentScanningDisableParams{} + _, err := r.client.V1.ContentScanningDisable(ctx, zoneID, params) + if err != nil { + resp.Diagnostics.AddError("Error disabling (Update) Content Scanning", err.Error()) + return + } +} + +func (r *ContentScanningResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // req.ID is the zoneID for which you want to import the state of the Content Scanning feature + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("zone_id"), req.ID)...) +} diff --git a/internal/framework/service/content_scanning/resource_test.go b/internal/framework/service/content_scanning/resource_test.go new file mode 100644 index 0000000000..e8c8eadfc9 --- /dev/null +++ b/internal/framework/service/content_scanning/resource_test.go @@ -0,0 +1,89 @@ +package content_scanning_test + +import ( + "context" + "errors" + "fmt" + "log" + "os" + "testing" + + cfv1 "github.com/cloudflare/cloudflare-go" + "github.com/cloudflare/terraform-provider-cloudflare/internal/acctest" + "github.com/cloudflare/terraform-provider-cloudflare/internal/utils" + + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func init() { + resource.AddTestSweepers("cloudflare_content_scanning", &resource.Sweeper{ + Name: "cloudflare_content_scanning", + F: testSweepCloudflareCS, + }) +} + +func testSweepCloudflareCS(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)) + } + + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + if zoneID == "" { + return errors.New("CLOUDFLARE_ZONE_ID must be set") + } + + status, err := client.ContentScanningStatus(ctx, cfv1.ZoneIdentifier(zoneID), cfv1.ContentScanningStatusParams{}) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Failed to GET Content Scanning status: %s", err)) + } + + if status.Result.Value == "disabled" { + log.Print("[DEBUG] Content Scanning already disabled") + return nil + } + _, err = client.ContentScanningDisable(ctx, cfv1.ZoneIdentifier(zoneID), cfv1.ContentScanningDisableParams{}) + if err != nil { + tflog.Error(ctx, fmt.Sprintf("Failed to disable Content Scanning: %s", err)) + return err + } + + return nil +} + +func TestAccCloudflareContentScanning_Basic(t *testing.T) { + rnd := utils.GenerateRandomResourceName() + name := fmt.Sprintf("cloudflare_content_scanning.%s", rnd) + zoneID := os.Getenv("CLOUDFLARE_ZONE_ID") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.TestAccPreCheck(t) + }, + ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccContentScanningSimple(rnd, zoneID, "true"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "enabled", "true"), + ), + }, + { + Config: testAccContentScanningSimple(rnd, zoneID, "false"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(name, "enabled", "false"), + ), + }, + }, + }) +} + +func testAccContentScanningSimple(ID, zoneID, enabled string) string { + return fmt.Sprintf(` + resource "cloudflare_content_scanning" "%[1]s" { + zone_id = "%[2]s" + enabled = "%[3]s" + }`, ID, zoneID, enabled) +} diff --git a/internal/framework/service/content_scanning/schema.go b/internal/framework/service/content_scanning/schema.go new file mode 100644 index 0000000000..761bf9fede --- /dev/null +++ b/internal/framework/service/content_scanning/schema.go @@ -0,0 +1,25 @@ +package content_scanning + +import ( + "context" + + "github.com/cloudflare/terraform-provider-cloudflare/internal/consts" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +func (r *ContentScanningResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Provides a Content Scanning resource to be used for managing the status of the Content Scanning feature within a specific zone.", + Attributes: map[string]schema.Attribute{ + consts.ZoneIDSchemaKey: schema.StringAttribute{ + Description: consts.ZoneIDSchemaDescription, + Required: true, + }, + "enabled": schema.BoolAttribute{ + Description: "State of the Content Scanning feature", + Required: true, + }, + }, + } +}