diff --git a/linode/networkingips/famework_datasource_schema.go b/linode/networkingips/famework_datasource_schema.go index 2aefea03d..2a6088b6e 100644 --- a/linode/networkingips/famework_datasource_schema.go +++ b/linode/networkingips/famework_datasource_schema.go @@ -1,82 +1,82 @@ package networkingips import ( - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/terraform-provider-linode/v2/linode/helper/frameworkfilter" ) -var updatedIPObjectType = types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "address": types.StringType, - "region": types.StringType, - "gateway": types.StringType, - "subnet_mask": types.StringType, - "prefix": types.Int64Type, - "type": types.StringType, - "public": types.BoolType, - "rdns": types.StringType, - "linode_id": types.Int64Type, - "reserved": types.BoolType, - }, +var filterConfig = frameworkfilter.Config{ + "type": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeString}, + "region": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeString}, + "rdns": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeString}, + "address": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeString}, + "prefix": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeInt}, + + "gateway": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeString}, + "subnet_mask": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeString}, + "public": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeString}, + "linode_id": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeInt}, + "reserved": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeBool}, } var frameworkDatasourceSchema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "address": schema.StringAttribute{ - Description: "The IP address.", - // Required: true, - Optional: true, - }, - "gateway": schema.StringAttribute{ - Description: "The default gateway for this address.", - Computed: true, - }, - "subnet_mask": schema.StringAttribute{ - Description: "The mask that separates host bits from network bits for this address.", - Computed: true, - }, - "prefix": schema.Int64Attribute{ - Description: "The number of bits set in the subnet mask.", - Computed: true, - }, - "type": schema.StringAttribute{ - Description: "The type of address this is (ipv4, ipv6, ipv6/pool, ipv6/range).", - Computed: true, - }, - "public": schema.BoolAttribute{ - Description: "Whether this is a public or private IP address.", - Computed: true, - }, - "rdns": schema.StringAttribute{ - Description: "The reverse DNS assigned to this address. For public IPv4 addresses, this will be set to " + - "a default value provided by Linode if not explicitly set.", - Computed: true, - }, - "linode_id": schema.Int64Attribute{ - Description: "The ID of the Linode this address currently belongs to.", - Computed: true, - }, - "region": schema.StringAttribute{ - Description: "The Region this IP address resides in.", - Computed: true, - }, "id": schema.StringAttribute{ - Description: "A unique identifier for this datasource.", + Description: "The data source's unique ID.", Computed: true, }, - "reserved": schema.BoolAttribute{ - Computed: true, - Description: "Whether this IP is reserved or not.", - }, - "ip_addresses": schema.ListAttribute{ - Description: "A list of all IPs.", - Computed: true, - ElementType: updatedIPObjectType, - }, - "filter_reserved": schema.BoolAttribute{ - Description: "Filter IPs by reserved status.", - Optional: true, + "order": filterConfig.OrderSchema(), + "order_by": filterConfig.OrderBySchema(), + }, + Blocks: map[string]schema.Block{ + "filter": filterConfig.Schema(), + "ip_addresses": schema.ListNestedBlock{ + Description: "The returned list of Images.", + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "address": schema.StringAttribute{ + Description: "The IP address.", + Computed: true, + }, + "gateway": schema.StringAttribute{ + Description: "The default gateway for this address.", + Computed: true, + }, + "subnet_mask": schema.StringAttribute{ + Description: "The mask that separates host bits from network bits for this address.", + Computed: true, + }, + "prefix": schema.Int64Attribute{ + Description: "The number of bits set in the subnet mask.", + Computed: true, + }, + "type": schema.StringAttribute{ + Description: "The type of address this is (ipv4, ipv6, ipv6/pool, ipv6/range).", + Computed: true, + }, + "public": schema.BoolAttribute{ + Description: "Whether this is a public or private IP address.", + Computed: true, + }, + "rdns": schema.StringAttribute{ + Description: "The reverse DNS assigned to this address. For public IPv4 addresses, this will be set to " + + "a default value provided by Linode if not explicitly set.", + Computed: true, + }, + "linode_id": schema.Int64Attribute{ + Description: "The ID of the Linode this address currently belongs to.", + Computed: true, + }, + "region": schema.StringAttribute{ + Description: "The Region this IP address resides in.", + Computed: true, + }, + "reserved": schema.BoolAttribute{ + Computed: true, + Description: "Whether this IP is reserved or not.", + }, + }, + }, }, }, } diff --git a/linode/networkingips/framework_datasource.go b/linode/networkingips/framework_datasource.go index 8070f4da4..7ed5a1e82 100644 --- a/linode/networkingips/framework_datasource.go +++ b/linode/networkingips/framework_datasource.go @@ -2,19 +2,18 @@ package networkingips import ( "context" - "encoding/json" - "fmt" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types" "github.com/linode/linodego" "github.com/linode/terraform-provider-linode/v2/linode/helper" ) +type DataSource struct { + helper.BaseDataSource +} + func NewDataSource() datasource.DataSource { return &DataSource{ BaseDataSource: helper.NewBaseDataSource( @@ -26,104 +25,57 @@ func NewDataSource() datasource.DataSource { } } -type DataSource struct { - helper.BaseDataSource -} - -type DataSourceModel struct { - Address types.String `tfsdk:"address"` - Gateway types.String `tfsdk:"gateway"` - SubnetMask types.String `tfsdk:"subnet_mask"` - Prefix types.Int64 `tfsdk:"prefix"` - Type types.String `tfsdk:"type"` - Public types.Bool `tfsdk:"public"` - RDNS types.String `tfsdk:"rdns"` - LinodeID types.Int64 `tfsdk:"linode_id"` - Region types.String `tfsdk:"region"` - ID types.String `tfsdk:"id"` - Reserved types.Bool `tfsdk:"reserved"` - IPAddresses types.List `tfsdk:"ip_addresses"` - FilterReserved types.Bool `tfsdk:"filter_reserved"` -} - func (d *DataSource) Read( ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse, ) { - tflog.Debug(ctx, "Read data.linode_networking_ip") + tflog.Debug(ctx, "Read data.linode_networking_ips") - var data DataSourceModel + var data FilterModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - if data.Address.IsNull() { - - // List all IP addresses with filter on reservation status - - filter, err := buildFilter(data) - if err != nil { - resp.Diagnostics.AddError("Unable to build filter", err.Error()) - return - } - - tflog.Debug(ctx, "Generated filter", map[string]interface{}{ - "filter": filter, - }) - - opts := &linodego.ListOptions{Filter: filter} - ips, err := d.Meta.Client.ListIPAddresses(ctx, opts) - if err != nil { - resp.Diagnostics.AddError("Unable to list IP Addresses", err.Error()) - return - } - - ipList := make([]attr.Value, len(ips)) - for i, ip := range ips { - ipObj := map[string]attr.Value{ - "address": types.StringValue(ip.Address), - "region": types.StringValue(ip.Region), - "gateway": types.StringValue(ip.Gateway), - "subnet_mask": types.StringValue(ip.SubnetMask), - "prefix": types.Int64Value(int64(ip.Prefix)), - "type": types.StringValue(string(ip.Type)), - "public": types.BoolValue(ip.Public), - "rdns": types.StringValue(ip.RDNS), - "linode_id": types.Int64Value(int64(ip.LinodeID)), - "reserved": types.BoolValue(ip.Reserved), - } - ipList[i] = types.ObjectValueMust(updatedIPObjectType.AttrTypes, ipObj) - } - - var diags diag.Diagnostics - data.IPAddresses, diags = types.ListValue(updatedIPObjectType, ipList) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + id, diag := filterConfig.GenerateID(data.Filters) + if diag != nil { + resp.Diagnostics.Append(diag) + return } + data.ID = id - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} - -func buildFilter(data DataSourceModel) (string, error) { - filters := make(map[string]string) - - if !data.FilterReserved.IsNull() { - filters["reserved"] = fmt.Sprintf("%t", data.FilterReserved.ValueBool()) + result, diag := filterConfig.GetAndFilter( + ctx, d.Meta.Client, data.Filters, listFunc, + data.Order, data.OrderBy) + if diag != nil { + resp.Diagnostics.Append(diag) + return } - if len(filters) == 0 { - return "", nil + resp.Diagnostics.Append(data.parseIPAddresses(helper.AnySliceToTyped[linodego.InstanceIP](result))...) + if resp.Diagnostics.HasError() { + return } + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} - jsonFilter, err := json.Marshal(filters) +func listFunc( + ctx context.Context, + client *linodego.Client, + filter string, +) ([]any, error) { + tflog.Trace(ctx, "client.ListIPAddresses(...)", map[string]any{ + "filter": filter, + }) + + images, err := client.ListIPAddresses(ctx, &linodego.ListOptions{ + Filter: filter, + }) if err != nil { - return "", fmt.Errorf("error creating filter: %v", err) + return nil, err } - return string(jsonFilter), nil + return helper.TypedSliceToAny(images), nil } diff --git a/linode/networkingips/framework_datasource_models.go b/linode/networkingips/framework_datasource_models.go new file mode 100644 index 000000000..43a07b971 --- /dev/null +++ b/linode/networkingips/framework_datasource_models.go @@ -0,0 +1,60 @@ +package networkingips + +import ( + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" + "github.com/linode/terraform-provider-linode/v2/linode/helper/frameworkfilter" +) + +type IPAddressModel struct { + Address types.String `tfsdk:"address"` + Type types.String `tfsdk:"type"` + Region types.String `tfsdk:"region"` + RDNS types.String `tfsdk:"rdns"` + Prefix types.Int64 `tfsdk:"prefix"` + Gateway types.String `tfsdk:"gateway"` + SubnetMask types.String `tfsdk:"subnet_mask"` + Public types.Bool `tfsdk:"public"` + LinodeID types.Int64 `tfsdk:"linode_id"` + Reserved types.Bool `tfsdk:"reserved"` +} + +func (m *IPAddressModel) ParseIP(ip linodego.InstanceIP) { + m.Address = types.StringValue(ip.Address) + m.Type = types.StringValue(string(ip.Type)) + m.Region = types.StringValue(ip.Region) + m.RDNS = types.StringValue(ip.RDNS) + m.Prefix = types.Int64Value(int64(ip.Prefix)) + m.Gateway = types.StringValue(ip.Gateway) + m.SubnetMask = types.StringValue(ip.SubnetMask) + m.Public = types.BoolValue(ip.Public) + m.LinodeID = types.Int64Value(int64(ip.LinodeID)) + m.Reserved = types.BoolValue(ip.Reserved) +} + +// FilterModel describes the Terraform resource data model to match the +// resource schema. +type FilterModel struct { + ID types.String `tfsdk:"id"` + Filters frameworkfilter.FiltersModelType `tfsdk:"filter"` + Order types.String `tfsdk:"order"` + OrderBy types.String `tfsdk:"order_by"` + IPAddresses []IPAddressModel `tfsdk:"ip_addresses"` +} + +func (data *FilterModel) parseIPAddresses( + ips []linodego.InstanceIP, +) diag.Diagnostics { + result := make([]IPAddressModel, len(ips)) + + for i := range ips { + var data IPAddressModel + data.ParseIP(ips[i]) + result[i] = data + } + + data.IPAddresses = result + + return nil +} diff --git a/linode/networkingips/tmpl/data_filter.gotf b/linode/networkingips/tmpl/data_filter.gotf index bb982222e..57139b64a 100644 --- a/linode/networkingips/tmpl/data_filter.gotf +++ b/linode/networkingips/tmpl/data_filter.gotf @@ -1,7 +1,19 @@ {{ define "networking_ip_data_filtered" }} +resource "linode_networking_ip" "test" { + type = "ipv4" + region = "us-mia" + reserved = true + public = true +} + data "linode_networking_ips" "filtered" { - filter_reserved = true + depends_on = [linode_networking_ip.test] + + filter { + name = "reserved" + values = ["true"] + } } {{ end }} \ No newline at end of file diff --git a/linode/networkingips/tmpl/data_list.gotf b/linode/networkingips/tmpl/data_list.gotf index 9258eb4dd..866041424 100644 --- a/linode/networkingips/tmpl/data_list.gotf +++ b/linode/networkingips/tmpl/data_list.gotf @@ -1,6 +1,15 @@ {{ define "networking_ip_data_list" }} +resource "linode_networking_ip" "test" { + type = "ipv4" + region = "us-mia" + reserved = true + public = true + +} + data "linode_networking_ips" "list" { + depends_on = [linode_networking_ip.test] } {{ end }} \ No newline at end of file