Skip to content

Commit

Permalink
Add models for nested objects and add type conversions between basety…
Browse files Browse the repository at this point in the history
…pes.ObjectValue and cloudflare.InfrastructureAccessTargetIPInfo
  • Loading branch information
SaiDadireddy committed Sep 23, 2024
1 parent 9ab1ce4 commit 68b5e7c
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (d *InfrastructureAccessTargetDataSource) Read(ctx context.Context, req dat
return &s
}
}
params := cloudflare.TargetListParams{
params := cloudflare.InfrastructureAccessTargetListParams{
Hostname: *checkSetNil(data.Hostname.String()),
HostnameContains: *checkSetNil(data.HostnameContains.String()),
IPV4: *checkSetNil(data.IPV4.String()),
Expand All @@ -86,7 +86,7 @@ func (d *InfrastructureAccessTargetDataSource) Read(ctx context.Context, req dat
AccountID: types.StringValue(accountId),
Hostname: types.StringValue(target.Hostname),
ID: types.StringValue(target.ID),
IP: target.IP,
IP: convertIPInfoToBaseTypeObject(target.IP),
CreatedAt: types.StringValue(target.CreatedAt),
ModifiedAt: types.StringValue(target.ModifiedAt),
})
Expand Down
23 changes: 16 additions & 7 deletions internal/framework/service/infrastructure_access_target/model.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
package infrastructure_access_target

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

// Resource model
type InfrastructureAccessTargetModel struct {
AccountID types.String `tfsdk:"account_id"`
Hostname types.String `tfsdk:"hostname"`
ID types.String `tfsdk:"id"`
IP cloudflare.IPInfo `tfsdk:"ip"`
CreatedAt types.String `tfsdk:"hostname"`
ModifiedAt types.String `tfsdk:"hostname"`
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 `json:"ipv4,omitempty"`
IPV6 types.Object `json:"ipv6,omitempty"`
}

type InfrastructureAccessTargetIPDetailsModel struct {
IPAddr string `json:"ip_addr"`
VirtualNetworkId string `json:"virtual_network_id"`
}

// Data source model
Expand Down
137 changes: 133 additions & 4 deletions internal/framework/service/infrastructure_access_target/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"github.com/cloudflare/cloudflare-go"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/flatteners"
"github.com/cloudflare/terraform-provider-cloudflare/internal/framework/muxclient"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/resource"
tftypes "github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

Expand Down Expand Up @@ -59,10 +61,15 @@ func (r *InfrastructureAccessTargetResource) Create(ctx context.Context, req res
resp.Diagnostics.AddError("failed to create infrastructure access target", "account id cannot be an empty string")
return
}
ipInfo, err := validateParseIPInfoCreate(ctx, data, resp)
if err != nil {
resp.Diagnostics.AddError("failed to create infrastructure access target", "account id cannot be an empty string")
return
}
createTargetParams := cloudflare.CreateInfrastructureAccessTargetParams{
InfrastructureAccessTargetParams: cloudflare.InfrastructureAccessTargetParams{
Hostname: data.Hostname.String(),
IP: data.IP,
IP: ipInfo,
},
}

Expand Down Expand Up @@ -114,11 +121,16 @@ func (r *InfrastructureAccessTargetResource) Update(ctx context.Context, req res
resp.Diagnostics.AddError("failed to update infrastructure access target", "account id cannot be an empty string")
return
}
ipInfo, err := validateParseIPInfoUpdate(ctx, data, resp)
if err != nil {
resp.Diagnostics.AddError("failed to create infrastructure access target", "account id cannot be an empty string")
return
}
updatedTargetParams := cloudflare.UpdateInfrastructureAccessTargetParams{
ID: data.ID.String(),
ModifyParams: cloudflare.InfrastructureAccessTargetParams{
Hostname: data.Hostname.String(),
IP: data.IP,
IP: ipInfo,
},
}

Expand Down Expand Up @@ -149,14 +161,131 @@ func (r *InfrastructureAccessTargetResource) Delete(ctx context.Context, req res
}
}

func buildTargetModelFromResponse(accountID tftypes.String, target cloudflare.Target) *InfrastructureAccessTargetModel {
func validateParseIPInfoCreate(ctx context.Context, data *InfrastructureAccessTargetModel, resp *resource.CreateResponse) (cloudflare.InfrastructureAccessTargetIPInfo, error) {
var ipInfo *InfrastructureAccessTargetIPInfoModel
resp.Diagnostics.Append(data.IP.As(ctx, &ipInfo, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)

var ipv4Details *InfrastructureAccessTargetIPDetailsModel
resp.Diagnostics.Append(ipInfo.IPV4.As(ctx, &ipv4Details, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)
var ipv6Details *InfrastructureAccessTargetIPDetailsModel
resp.Diagnostics.Append(ipInfo.IPV6.As(ctx, &ipv6Details, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)

return validateParseIPInfo(ipv4Details, ipv6Details)
}

func validateParseIPInfoUpdate(ctx context.Context, data *InfrastructureAccessTargetModel, resp *resource.UpdateResponse) (cloudflare.InfrastructureAccessTargetIPInfo, error) {
var ipInfo *InfrastructureAccessTargetIPInfoModel
resp.Diagnostics.Append(data.IP.As(ctx, &ipInfo, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)

var ipv4Details *InfrastructureAccessTargetIPDetailsModel
resp.Diagnostics.Append(ipInfo.IPV4.As(ctx, &ipv4Details, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)
var ipv6Details *InfrastructureAccessTargetIPDetailsModel
resp.Diagnostics.Append(ipInfo.IPV6.As(ctx, &ipv6Details, basetypes.ObjectAsOptions{UnhandledNullAsEmpty: true, UnhandledUnknownAsEmpty: true})...)

return validateParseIPInfo(ipv4Details, ipv6Details)
}

func validateParseIPInfo(ipv4Details *InfrastructureAccessTargetIPDetailsModel, ipv6Details *InfrastructureAccessTargetIPDetailsModel) (cloudflare.InfrastructureAccessTargetIPInfo, error) {
if ipv4Details == nil && ipv6Details == nil {
return cloudflare.InfrastructureAccessTargetIPInfo{}, fmt.Errorf("error creating target resource: one of ipv4 or ipv6 must be configured")
}

if ipv4Details != nil && ipv6Details != nil {
return cloudflare.InfrastructureAccessTargetIPInfo{
IPV4: &cloudflare.InfrastructureAccessTargetIPDetails{
IPAddr: ipv4Details.IPAddr,
VirtualNetworkId: ipv4Details.VirtualNetworkId,
},
IPV6: &cloudflare.InfrastructureAccessTargetIPDetails{
IPAddr: ipv6Details.IPAddr,
VirtualNetworkId: ipv6Details.VirtualNetworkId,
},
}, nil
} else if ipv4Details != nil {
return cloudflare.InfrastructureAccessTargetIPInfo{
IPV4: &cloudflare.InfrastructureAccessTargetIPDetails{
IPAddr: ipv4Details.IPAddr,
VirtualNetworkId: ipv4Details.VirtualNetworkId,
},
}, nil
} else {
return cloudflare.InfrastructureAccessTargetIPInfo{
IPV6: &cloudflare.InfrastructureAccessTargetIPDetails{
IPAddr: ipv6Details.IPAddr,
VirtualNetworkId: ipv6Details.VirtualNetworkId,
},
}, nil
}
}

func buildTargetModelFromResponse(accountID tftypes.String, target cloudflare.InfrastructureAccessTarget) *InfrastructureAccessTargetModel {
built := InfrastructureAccessTargetModel{
AccountID: accountID,
Hostname: flatteners.String(target.Hostname),
ID: flatteners.String(target.ID),
IP: target.IP,
IP: convertIPInfoToBaseTypeObject(target.IP),
CreatedAt: flatteners.String(target.CreatedAt),
ModifiedAt: flatteners.String(target.ModifiedAt),
}
return &built
}

func convertIPInfoToBaseTypeObject(ipInfo cloudflare.InfrastructureAccessTargetIPInfo) basetypes.ObjectValue {
if ipInfo.IPV4 != nil && ipInfo.IPV6 != nil {
ipv4Object := buildObjectFromIpDetails(ipInfo.IPV4.IPAddr, ipInfo.IPV4.VirtualNetworkId)
ipv6Object := buildObjectFromIpDetails(ipInfo.IPV6.IPAddr, ipInfo.IPV6.VirtualNetworkId)
parentObjectMap := map[string]attr.Value{
"ipv4": ipv4Object,
"ipv6": ipv6Object,
}
parentObjectValue, _ := tftypes.ObjectValue(map[string]attr.Type{
"ipv4": tftypes.ObjectType{
AttrTypes: map[string]attr.Type{
"ip_addr": tftypes.StringType,
"virtual_network_id": tftypes.StringType,
},
},
"ipv6": tftypes.ObjectType{
AttrTypes: map[string]attr.Type{
"ip_addr": tftypes.StringType,
"virtual_network_id": tftypes.StringType,
},
},
}, parentObjectMap)
return parentObjectValue
} else if ipInfo.IPV4 != nil {
ipv4Object := buildObjectFromIpDetails(ipInfo.IPV4.IPAddr, ipInfo.IPV4.VirtualNetworkId)
return buildObjectFromIpInfo("ipv4", ipv4Object)
} else {
ipv6Object := buildObjectFromIpDetails(ipInfo.IPV6.IPAddr, ipInfo.IPV6.VirtualNetworkId)
return buildObjectFromIpInfo("ipv6", ipv6Object)
}
}

func buildObjectFromIpDetails(ipAddr string, virtualNetworkId string) basetypes.ObjectValue {
ipDetailsAttributes := map[string]attr.Value{
"ip_addr": flatteners.String(ipAddr),
"virtual_network_id": flatteners.String(virtualNetworkId),
}
ipDetailsObjectType, _ := tftypes.ObjectValue(map[string]attr.Type{
"ip_addr": tftypes.StringType,
"virtual_network_id": tftypes.StringType,
}, ipDetailsAttributes)

return ipDetailsObjectType
}

func buildObjectFromIpInfo(ipv string, baseObjectMap basetypes.ObjectValue) basetypes.ObjectValue {
parentObjectMap := map[string]attr.Value{
ipv: baseObjectMap,
}
parentObjectValue, _ := tftypes.ObjectValue(map[string]attr.Type{
ipv: tftypes.ObjectType{
AttrTypes: map[string]attr.Type{
"ip_addr": tftypes.StringType,
"virtual_network_id": tftypes.StringType,
},
},
}, parentObjectMap)
return parentObjectValue
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func init() {

ctx := context.Background()
// Retrieve all targets created under the current test account
targets, _, err := client.ListInfrastructureAccessTargets(ctx, cloudflare.AccountIdentifier(accountID), cloudflare.TargetListParams{})
targets, _, err := client.ListInfrastructureAccessTargets(ctx, cloudflare.AccountIdentifier(accountID), cloudflare.InfrastructureAccessTargetListParams{})
if err != nil {
return fmt.Errorf("failed to fetch rulesets: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ func (r *InfrastructureAccessTargetResource) Schema(ctx context.Context, req res
MarkdownDescription: heredoc.Doc(`
The [Infrastructure Access Target](https://developers.cloudflare.com/cloudflare-one/insights/risk-score/) resource allows you to configure Cloudflare Risk Behaviors for an account.
`),

Attributes: map[string]schema.Attribute{
consts.AccountIDSchemaKey: schema.StringAttribute{
MarkdownDescription: consts.AccountIDSchemaDescription,
Expand All @@ -32,7 +31,6 @@ func (r *InfrastructureAccessTargetResource) Schema(ctx context.Context, req res
},
consts.IDSchemaKey: schema.StringAttribute{
Computed: true,
Optional: true,
MarkdownDescription: consts.IDSchemaDescription + " This is target's unique identifier.",
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
Expand Down

0 comments on commit 68b5e7c

Please sign in to comment.