Skip to content

Commit fcda183

Browse files
authored
TPT-4293: terraform: Implement linode_reserved_ip_types data source (#2322)
1 parent cca3ff1 commit fcda183

9 files changed

Lines changed: 397 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
page_title: "Linode: linode_reserved_ip_types"
3+
description: |-
4+
Provides information about Linode Reserved IP types that match a set of filters.
5+
---
6+
7+
# Data Source: linode\_reserved\_ip\_types
8+
9+
Provides information about Linode Reserved IP types that match a set of filters.
10+
For more information, see the [Linode APIv4 docs](https://techdocs.akamai.com/linode-api/reference/get-reserved-ip-types).
11+
12+
## Example Usage
13+
14+
Get information about all Reserved IP types:
15+
16+
```hcl
17+
data "linode_reserved_ip_types" "all" {}
18+
19+
output "type_ids" {
20+
value = data.linode_reserved_ip_types.all.types.*.id
21+
}
22+
```
23+
24+
Get information about a specific Reserved IP type by ID:
25+
26+
```hcl
27+
data "linode_reserved_ip_types" "specific" {
28+
filter {
29+
name = "id"
30+
values = ["reserved-ip"]
31+
}
32+
}
33+
34+
output "label" {
35+
value = data.linode_reserved_ip_types.specific.types[0].label
36+
}
37+
```
38+
39+
## Argument Reference
40+
41+
The following arguments are supported:
42+
43+
* [`filter`](#filter) - (Optional) A set of filters used to select Reserved IP types that meet certain requirements.
44+
45+
* `order_by` - (Optional) The attribute to order the results by. See the [Filterable Fields section](#filterable-fields) for a list of valid fields.
46+
47+
* `order` - (Optional) The order in which results should be returned. (`asc`, `desc`; default `asc`)
48+
49+
### Filter
50+
51+
* `name` - (Required) The name of the field to filter by. See the [Filterable Fields section](#filterable-fields) for a complete list of filterable fields.
52+
53+
* `values` - (Required) A list of values for the filter to allow. These values should all be in string form.
54+
55+
* `match_by` - (Optional) The method to match the field by. (`exact`, `regex`, `substring`; default `exact`)
56+
57+
## Attributes Reference
58+
59+
Each Reserved IP type will be stored in the `types` attribute and will export the following attributes:
60+
61+
* `id` - The unique ID of this Reserved IP type (e.g. `reserved-ip`).
62+
63+
* `label` - The human-readable label for this Reserved IP type.
64+
65+
* `price.0.hourly` - Cost (in US dollars) per hour.
66+
67+
* `price.0.monthly` - Cost (in US dollars) per month.
68+
69+
* `region_prices.*.id` - The ID of the region this price entry applies to.
70+
71+
* `region_prices.*.hourly` - The hourly price for this Reserved IP type in the given region.
72+
73+
* `region_prices.*.monthly` - The monthly price for this Reserved IP type in the given region.
74+
75+
## Filterable Fields
76+
77+
* `label`

linode/framework_provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ import (
9696
"github.com/linode/terraform-provider-linode/v3/linode/regionsvpcavailability"
9797
"github.com/linode/terraform-provider-linode/v3/linode/regionvpcavailability"
9898
"github.com/linode/terraform-provider-linode/v3/linode/reservedip"
99+
"github.com/linode/terraform-provider-linode/v3/linode/reservediptypes"
99100
"github.com/linode/terraform-provider-linode/v3/linode/sshkey"
100101
"github.com/linode/terraform-provider-linode/v3/linode/sshkeys"
101102
"github.com/linode/terraform-provider-linode/v3/linode/stackscript"
@@ -379,5 +380,6 @@ func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource
379380
lkenodepool.NewDataSource,
380381
regionvpcavailability.NewDataSource,
381382
regionsvpcavailability.NewDataSource,
383+
reservediptypes.NewDataSource,
382384
}
383385
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//go:build integration || reservediptypes
2+
3+
package reservediptypes_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
10+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
11+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
12+
"github.com/linode/terraform-provider-linode/v3/linode/acceptance"
13+
"github.com/linode/terraform-provider-linode/v3/linode/reservediptypes/tmpl"
14+
)
15+
16+
func TestAccDataSourceReservedIPTypes_basic(t *testing.T) {
17+
t.Parallel()
18+
19+
dataSourceName := "data.linode_reserved_ip_types.foobar"
20+
21+
resource.Test(t, resource.TestCase{
22+
PreCheck: func() { acceptance.PreCheck(t) },
23+
ProtoV6ProviderFactories: acceptance.ProtoV6ProviderFactories,
24+
Steps: []resource.TestStep{
25+
{
26+
Config: tmpl.DataBasic(t),
27+
ConfigStateChecks: []statecheck.StateCheck{
28+
statecheck.ExpectKnownValue(
29+
dataSourceName,
30+
tfjsonpath.New("types"),
31+
knownvalue.NotNull(),
32+
),
33+
},
34+
},
35+
{
36+
Config: tmpl.DataFilter(t),
37+
ConfigStateChecks: []statecheck.StateCheck{
38+
statecheck.ExpectKnownValue(
39+
dataSourceName,
40+
tfjsonpath.New("types"),
41+
knownvalue.ListSizeExact(1),
42+
),
43+
statecheck.ExpectKnownValue(
44+
dataSourceName,
45+
tfjsonpath.New("types").AtSliceIndex(0).AtMapKey("id"),
46+
knownvalue.StringExact("reserved-ipv4"),
47+
),
48+
statecheck.ExpectKnownValue(
49+
dataSourceName,
50+
tfjsonpath.New("types").AtSliceIndex(0).AtMapKey("label"),
51+
knownvalue.NotNull(),
52+
),
53+
statecheck.ExpectKnownValue(
54+
dataSourceName,
55+
tfjsonpath.New("types").AtSliceIndex(0).AtMapKey("price").AtSliceIndex(0).AtMapKey("hourly"),
56+
knownvalue.NotNull(),
57+
),
58+
statecheck.ExpectKnownValue(
59+
dataSourceName,
60+
tfjsonpath.New("types").AtSliceIndex(0).AtMapKey("price").AtSliceIndex(0).AtMapKey("monthly"),
61+
knownvalue.NotNull(),
62+
),
63+
},
64+
},
65+
},
66+
})
67+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package reservediptypes
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/datasource"
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
8+
"github.com/linode/linodego"
9+
"github.com/linode/terraform-provider-linode/v3/linode/helper"
10+
)
11+
12+
func NewDataSource() datasource.DataSource {
13+
return &DataSource{
14+
BaseDataSource: helper.NewBaseDataSource(
15+
helper.BaseDataSourceConfig{
16+
Name: "linode_reserved_ip_types",
17+
Schema: &frameworkDataSourceSchema,
18+
},
19+
),
20+
}
21+
}
22+
23+
type DataSource struct {
24+
helper.BaseDataSource
25+
}
26+
27+
func (r *DataSource) Read(
28+
ctx context.Context,
29+
req datasource.ReadRequest,
30+
resp *datasource.ReadResponse,
31+
) {
32+
tflog.Debug(ctx, "Read data."+r.Config.Name)
33+
34+
var data reservedIPTypeFilterModel
35+
36+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
37+
if resp.Diagnostics.HasError() {
38+
return
39+
}
40+
41+
id, d := filterConfig.GenerateID(data.Filters)
42+
if d != nil {
43+
resp.Diagnostics.Append(d)
44+
return
45+
}
46+
data.ID = id
47+
48+
result, d := filterConfig.GetAndFilter(
49+
ctx, r.Meta.Client, data.Filters, listReservedIPTypes, data.Order, data.OrderBy)
50+
if d != nil {
51+
resp.Diagnostics.Append(d)
52+
return
53+
}
54+
55+
diags := data.parseReservedIPTypes(ctx, helper.AnySliceToTyped[linodego.ReservedIPType](result))
56+
resp.Diagnostics.Append(diags...)
57+
if resp.Diagnostics.HasError() {
58+
return
59+
}
60+
61+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
62+
}
63+
64+
func listReservedIPTypes(ctx context.Context, client *linodego.Client, filter string) ([]any, error) {
65+
tflog.Debug(ctx, "Listing Reserved IP types", map[string]any{
66+
"filter_header": filter,
67+
})
68+
69+
types, err := client.ListReservedIPTypes(ctx, &linodego.ListOptions{
70+
Filter: filter,
71+
})
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
return helper.TypedSliceToAny(types), nil
77+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package reservediptypes
2+
3+
import (
4+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
5+
"github.com/linode/terraform-provider-linode/v3/linode/helper"
6+
"github.com/linode/terraform-provider-linode/v3/linode/helper/frameworkfilter"
7+
)
8+
9+
var filterConfig = frameworkfilter.Config{
10+
"id": {APIFilterable: false, TypeFunc: frameworkfilter.FilterTypeString},
11+
"label": {APIFilterable: true, TypeFunc: frameworkfilter.FilterTypeString},
12+
}
13+
14+
var frameworkDataSourceSchema = schema.Schema{
15+
Attributes: map[string]schema.Attribute{
16+
"id": schema.StringAttribute{
17+
Description: "The data source's unique ID.",
18+
Computed: true,
19+
},
20+
"order_by": filterConfig.OrderBySchema(),
21+
"order": filterConfig.OrderSchema(),
22+
"types": schema.ListNestedAttribute{
23+
Description: "The returned list of Reserved IP types.",
24+
Computed: true,
25+
NestedObject: schema.NestedAttributeObject{
26+
Attributes: map[string]schema.Attribute{
27+
"id": schema.StringAttribute{
28+
Description: "The unique ID assigned to this Reserved IP Type.",
29+
Required: true,
30+
},
31+
"label": schema.StringAttribute{
32+
Description: "The Reserved IP Type's label.",
33+
Computed: true,
34+
},
35+
"price": schema.ListAttribute{
36+
Description: "Cost in US dollars, broken down into hourly and monthly charges.",
37+
Computed: true,
38+
ElementType: helper.PriceObjectType,
39+
},
40+
"region_prices": schema.ListAttribute{
41+
Description: "A list of region-specific prices for this Reserved IP Type.",
42+
Computed: true,
43+
ElementType: helper.RegionPriceObjectType,
44+
},
45+
},
46+
},
47+
},
48+
},
49+
Blocks: map[string]schema.Block{
50+
"filter": filterConfig.Schema(),
51+
},
52+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package reservediptypes
2+
3+
import (
4+
"context"
5+
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
"github.com/linode/linodego"
9+
"github.com/linode/terraform-provider-linode/v3/linode/helper"
10+
"github.com/linode/terraform-provider-linode/v3/linode/helper/frameworkfilter"
11+
)
12+
13+
// priceModel is the tfsdk-tagged struct for a single price entry.
14+
type priceModel struct {
15+
Hourly types.Float64 `tfsdk:"hourly"`
16+
Monthly types.Float64 `tfsdk:"monthly"`
17+
}
18+
19+
// regionPriceModel is the tfsdk-tagged struct for a region-specific price entry.
20+
type regionPriceModel struct {
21+
ID types.String `tfsdk:"id"`
22+
Hourly types.Float64 `tfsdk:"hourly"`
23+
Monthly types.Float64 `tfsdk:"monthly"`
24+
}
25+
26+
type dataSourceModel struct {
27+
ID types.String `tfsdk:"id"`
28+
Label types.String `tfsdk:"label"`
29+
Price types.List `tfsdk:"price"`
30+
RegionPrices types.List `tfsdk:"region_prices"`
31+
}
32+
33+
func (data *dataSourceModel) parseReservedIPType(ctx context.Context, ipType *linodego.ReservedIPType) diag.Diagnostics {
34+
data.ID = types.StringValue(ipType.ID)
35+
data.Label = types.StringValue(ipType.Label)
36+
37+
price, diags := types.ListValueFrom(ctx, helper.PriceObjectType, []priceModel{
38+
{
39+
Hourly: types.Float64Value(ipType.Price.Hourly),
40+
Monthly: types.Float64Value(ipType.Price.Monthly),
41+
},
42+
})
43+
if diags.HasError() {
44+
return diags
45+
}
46+
data.Price = price
47+
48+
regionPrices := make([]regionPriceModel, len(ipType.RegionPrices))
49+
for i, rp := range ipType.RegionPrices {
50+
regionPrices[i] = regionPriceModel{
51+
ID: types.StringValue(rp.ID),
52+
Hourly: types.Float64Value(rp.Hourly),
53+
Monthly: types.Float64Value(rp.Monthly),
54+
}
55+
}
56+
rpList, diags := types.ListValueFrom(ctx, helper.RegionPriceObjectType, regionPrices)
57+
if diags.HasError() {
58+
return diags
59+
}
60+
data.RegionPrices = rpList
61+
62+
return nil
63+
}
64+
65+
type reservedIPTypeFilterModel struct {
66+
ID types.String `tfsdk:"id"`
67+
Order types.String `tfsdk:"order"`
68+
OrderBy types.String `tfsdk:"order_by"`
69+
Filters frameworkfilter.FiltersModelType `tfsdk:"filter"`
70+
Types []dataSourceModel `tfsdk:"types"`
71+
}
72+
73+
func (model *reservedIPTypeFilterModel) parseReservedIPTypes(ctx context.Context, ipTypes []linodego.ReservedIPType) diag.Diagnostics {
74+
result := make([]dataSourceModel, len(ipTypes))
75+
76+
for i := range ipTypes {
77+
var m dataSourceModel
78+
79+
diags := m.parseReservedIPType(ctx, &ipTypes[i])
80+
if diags.HasError() {
81+
return diags
82+
}
83+
84+
result[i] = m
85+
}
86+
87+
model.Types = result
88+
89+
return nil
90+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{{ define "reserved_ip_types_data_basic" }}
2+
3+
data "linode_reserved_ip_types" "foobar" {}
4+
5+
{{ end }}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{ define "reserved_ip_types_data_filter" }}
2+
3+
data "linode_reserved_ip_types" "foobar" {
4+
filter {
5+
name = "id"
6+
values = ["reserved-ipv4"]
7+
}
8+
}
9+
10+
{{ end }}

0 commit comments

Comments
 (0)