Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add terraform resource and data source for Infrastructure Access Target #4077

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/4062.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:new-datasource
cloudflare_infrastructure_access_targets
```

```release-note:new-resource
cloudflare_infrastructure_access_target
```
3 changes: 3 additions & 0 deletions internal/framework/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/gateway_app_types"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/gateway_categories"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/hyperdrive_config"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/infrastructure_access_target"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/list_item"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/origin_ca_certificate"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/r2_bucket"
Expand Down Expand Up @@ -381,6 +382,7 @@ func (p *CloudflareProvider) Resources(ctx context.Context) []func() resource.Re
workers_for_platforms_dispatch_namespace_deprecated.NewResource,
workers_for_platforms_dispatch_namespace.NewResource,
zero_trust_risk_score_integration.NewResource,
infrastructure_access_target.NewResource,
}
}

Expand All @@ -393,6 +395,7 @@ func (p *CloudflareProvider) DataSources(ctx context.Context) []func() datasourc
gateway_categories.NewDataSource,
gateway_app_types.NewDataSource,
dcv_delegation.NewDataSource,
infrastructure_access_target.NewDataSource,
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package infrastructure_access_target

import (
"context"
"fmt"

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

var _ datasource.DataSource = &InfrastructureAccessTargetDataSource{}

func NewDataSource() datasource.DataSource {
return &InfrastructureAccessTargetDataSource{}
}

type InfrastructureAccessTargetDataSource struct {
client *muxclient.Client
}

func (d *InfrastructureAccessTargetDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_infrastructure_access_targets"
}

func (d *InfrastructureAccessTargetDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.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
}

d.client = client
}

func (d *InfrastructureAccessTargetDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data *InfrastructureAccessTargetsModel

resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
if resp.Diagnostics.HasError() {
return
}

accountId := data.AccountID.ValueString()
if accountId == "" {
resp.Diagnostics.AddError("failed to update infrastructure access target", "account id cannot be an empty string")
return
}
params := cloudflare.InfrastructureAccessTargetListParams{
Hostname: data.Hostname.ValueString(),
HostnameContains: data.HostnameContains.ValueString(),
IPV4: data.IPV4.ValueString(),
IPV6: data.IPV6.ValueString(),
CreatedAfter: data.CreatedAfter.ValueString(),
ModifedAfter: data.ModifiedAfter.ValueString(),
VirtualNetworkId: data.VirtualNetworkId.ValueString(),
}

allTargets, _, err := d.client.V1.ListInfrastructureAccessTargets(ctx, cloudflare.AccountIdentifier(accountId), params)
if err != nil {
resp.Diagnostics.AddError("failed to fetch Infrastructure Access Targets: %w", err.Error())
return
}
if len(allTargets) == 0 {
resp.Diagnostics.AddError("failed to fetch Infrastructure Access Targets", "no Infrastructure Access Targets matching given query parameters")
}

var targets []InfrastructureAccessTargetModel
for _, target := range allTargets {
targets = append(targets, InfrastructureAccessTargetModel{
AccountID: types.StringValue(accountId),
Hostname: types.StringValue(target.Hostname),
ID: types.StringValue(target.ID),
IP: convertIPInfoToBaseTypeObject(target.IP),
CreatedAt: types.StringValue(target.CreatedAt),
ModifiedAt: types.StringValue(target.ModifiedAt),
})
}

data.Targets = targets
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package infrastructure_access_target_test

import (
"fmt"
"os"
"testing"

"github.com/cloudflare/terraform-provider-cloudflare/internal/acctest"
"github.com/cloudflare/terraform-provider-cloudflare/internal/utils"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccCloudflareInfraAccessTarget_DataSource(t *testing.T) {
rnd1 := utils.GenerateRandomResourceName()
rnd2 := utils.GenerateRandomResourceName()

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.TestAccPreCheck(t) },
ProtoV6ProviderFactories: acctest.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testCloudflareInfrastructureTargetsMatchNoIpv6(rnd1),
Check: resource.ComposeTestCheckFunc(
// We should expect this data source to have 1 resource.
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets."+rnd1, "targets.#", "1"),
// Check that there is no ipv6 object in this resource.
resource.TestCheckNoResourceAttr("data.cloudflare_infrastructure_access_targets."+rnd1, "ip.ipv6"),
// Check the existing attributes of this resource.
resource.TestCheckResourceAttr("cloudflare_infrastructure_access_target."+rnd1, "hostname", rnd1),
resource.TestCheckResourceAttr("cloudflare_infrastructure_access_target."+rnd1, "hostname", rnd1),
resource.TestCheckResourceAttr("cloudflare_infrastructure_access_target."+rnd1, "ip.ipv4.ip_addr", "187.26.29.233"),
resource.TestCheckResourceAttr("cloudflare_infrastructure_access_target."+rnd1, "ip.ipv4.virtual_network_id", "b9c90134-52de-4903-81e8-004a3a06b435"),
),
},
{
Config: testCloudflareInfrastructureTargetsMatchAll(rnd1, rnd2),
Check: resource.ComposeTestCheckFunc(
// Expect this data source to have 2 resources.
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.#", "2"),
// Check the attributes of the first resource.
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.0.hostname", rnd2),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.0.ip.ipv4.ip_addr", "250.26.29.250"),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.0.ip.ipv6.ip_addr", "64c0:64e8:f0b4:8dbf:7104:72b0:ec8f:f5e0"),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.0.ip.ipv6.virtual_network_id", "b9c90134-52de-4903-81e8-004a3a06b435"),
/// Check the attributes of the second resource.
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.1.hostname", rnd1),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.1.ip.ipv4.ip_addr", "187.26.29.249"),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.1.ip.ipv4.virtual_network_id", "b9c90134-52de-4903-81e8-004a3a06b435"),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.1.ip.ipv6.ip_addr", "64c0:64e8:f0b4:8dbf:7104:72b0:ec8f:f5e0"),
resource.TestCheckResourceAttr("data.cloudflare_infrastructure_access_targets.all", "targets.1.ip.ipv6.virtual_network_id", "b9c90134-52de-4903-81e8-004a3a06b435"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after fixing this, i think this is a legit failure

=== RUN   TestAccCloudflareInfraAccessTarget_DataSource
    data_source_test.go:17: Step 2/2 error: Check failed: Check 9/10 error: data.cloudflare_infrastructure_access_targets.all: Attribute 'targets.1.ip.ipv6.ip_addr' not found
--- FAIL: TestAccCloudflareInfraAccessTarget_DataSource (4.29s)
FAIL
FAIL	github.com/cloudflare/terraform-provider-cloudflare/internal/framework/service/infrastructure_access_target	5.389s
FAIL

Copy link
Contributor Author

@SaiDadireddy SaiDadireddy Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested the second test manually and it's working as expected. Asserting in this case is tricky with multiple resources in the returned data source since there is no strict ordering. Removed the second test, it's not necessary.

),
},
},
})
}

func testCloudflareInfrastructureTargetsMatchNoIpv6(hostname string) string {
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
return fmt.Sprintf(`
resource "cloudflare_infrastructure_access_target" "%[2]s" {
account_id = "%[1]s"
hostname = "%[2]s"
ip = {
ipv4 = {
ip_addr = "187.26.29.233",
virtual_network_id = "b9c90134-52de-4903-81e8-004a3a06b435"
}
}
}

data "cloudflare_infrastructure_access_targets" "%[2]s" {
depends_on = [cloudflare_infrastructure_access_target.%[2]s]
account_id = "%[1]s"
}
`, accountID, hostname)
}

func testCloudflareInfrastructureTargetsMatchAll(hostname1 string, hostname2 string) string {
accountID := os.Getenv("CLOUDFLARE_ACCOUNT_ID")
return fmt.Sprintf(`
resource "cloudflare_infrastructure_access_target" "%[2]s" {
account_id = "%[1]s"
hostname = "%[2]s"
ip = {
ipv4 = {
ip_addr = "187.26.29.249",
virtual_network_id = "b9c90134-52de-4903-81e8-004a3a06b435"
}
}
}

resource "cloudflare_infrastructure_access_target" "%[3]s" {
account_id = "%[1]s"
hostname = "%[3]s"
ip = {
ipv4 = {
ip_addr = "250.26.29.250",
virtual_network_id = "b9c90134-52de-4903-81e8-004a3a06b435"
},
ipv6 = {
ip_addr = "64c0:64e8:f0b4:8dbf:7104:72b0:ec8f:f5e0",
virtual_network_id = "b9c90134-52de-4903-81e8-004a3a06b435"
}
}
}

data "cloudflare_infrastructure_access_targets" "all" {
depends_on = [
cloudflare_infrastructure_access_target.%[2]s,
cloudflare_infrastructure_access_target.%[3]s
]
account_id = "%[1]s"
}
`, accountID, hostname1, hostname2)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package infrastructure_access_target

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

type InfrastructureAccessTargetModel struct {
AccountID types.String `tfsdk:"account_id"`
Hostname types.String `tfsdk:"hostname"`
ID types.String `tfsdk:"id"`
IP types.Object `tfsdk:"ip"`
CreatedAt types.String `tfsdk:"created_at"`
ModifiedAt types.String `tfsdk:"modified_at"`
}

type InfrastructureAccessTargetIPInfoModel struct {
IPV4 types.Object `tfsdk:"ipv4"`
IPV6 types.Object `tfsdk:"ipv6"`
}

type InfrastructureAccessTargetIPDetailsModel struct {
IPAddr types.String `tfsdk:"ip_addr"`
VirtualNetworkId types.String `tfsdk:"virtual_network_id"`
}

type InfrastructureAccessTargetsModel struct {
AccountID types.String `tfsdk:"account_id"`
Hostname types.String `tfsdk:"hostname"`
HostnameContains types.String `tfsdk:"hostname_contains"`
IPV4 types.String `tfsdk:"ipv4"`
IPV6 types.String `tfsdk:"ipv6"`
VirtualNetworkId types.String `tfsdk:"virtual_network_id"`
CreatedAfter types.String `tfsdk:"created_after"`
ModifiedAfter types.String `tfsdk:"modified_after"`
Targets []InfrastructureAccessTargetModel `tfsdk:"targets"`
}
Loading
Loading