From c0451aa3615a1c73d2ec9a02fcb9ae6c2c6d77a5 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Thu, 26 Sep 2024 10:13:43 -0400 Subject: [PATCH 1/7] Registered the resource and data source within the provider --- linode/framework_provider.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linode/framework_provider.go b/linode/framework_provider.go index 22cab83ff..0b123c5ae 100644 --- a/linode/framework_provider.go +++ b/linode/framework_provider.go @@ -3,6 +3,7 @@ package linode import ( "context" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips" "github.com/linode/terraform-provider-linode/v2/linode/vpcips" "github.com/hashicorp/terraform-plugin-framework/datasource" @@ -222,6 +223,7 @@ func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Res firewall.NewResource, placementgroup.NewResource, placementgroupassignment.NewResource, + networkreservedips.NewResource, } } @@ -286,5 +288,6 @@ func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource placementgroups.NewDataSource, childaccount.NewDataSource, childaccounts.NewDataSource, + networkreservedips.NewDataSource, } } From 3d09656a38720e1ac2a24860d5138165dbd5ff39 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Thu, 26 Sep 2024 10:16:35 -0400 Subject: [PATCH 2/7] Implemented framework_resource, framework_datasource along with their respective tests --- linode/networkreservedips/datasource_test.go | 57 ++++++ .../framework_datasource.go | 124 ++++++++++++ .../framework_datasource_schema.go | 91 +++++++++ linode/networkreservedips/framework_models.go | 69 +++++++ .../networkreservedips/framework_resource.go | 181 ++++++++++++++++++ linode/networkreservedips/framework_schema.go | 60 ++++++ linode/networkreservedips/resource_test.go | 59 ++++++ .../networkreservedips/tmpl/reserved_ip.gotf | 17 ++ .../tmpl/reserved_ip_basic.gotf | 7 + linode/networkreservedips/tmpl/template.go | 31 +++ 10 files changed, 696 insertions(+) create mode 100644 linode/networkreservedips/datasource_test.go create mode 100644 linode/networkreservedips/framework_datasource.go create mode 100644 linode/networkreservedips/framework_datasource_schema.go create mode 100644 linode/networkreservedips/framework_models.go create mode 100644 linode/networkreservedips/framework_resource.go create mode 100644 linode/networkreservedips/framework_schema.go create mode 100644 linode/networkreservedips/resource_test.go create mode 100644 linode/networkreservedips/tmpl/reserved_ip.gotf create mode 100644 linode/networkreservedips/tmpl/reserved_ip_basic.gotf create mode 100644 linode/networkreservedips/tmpl/template.go diff --git a/linode/networkreservedips/datasource_test.go b/linode/networkreservedips/datasource_test.go new file mode 100644 index 000000000..0826e3663 --- /dev/null +++ b/linode/networkreservedips/datasource_test.go @@ -0,0 +1,57 @@ +package networkreservedips_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/linode/terraform-provider-linode/v2/linode/acceptance" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips/tmpl" +) + +func TestAccDataSource_reservedIP(t *testing.T) { + t.Parallel() + + resourceName := "data.linode_reserved_ip.test" + region, _ := acceptance.GetRandomRegionWithCaps([]string{"linodes"}, "core") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: tmpl.DataBasic(t, region), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "address"), + resource.TestCheckResourceAttrSet(resourceName, "region"), + resource.TestCheckResourceAttrSet(resourceName, "gateway"), + resource.TestCheckResourceAttrSet(resourceName, "subnet_mask"), + resource.TestCheckResourceAttrSet(resourceName, "prefix"), + resource.TestCheckResourceAttrSet(resourceName, "type"), + resource.TestCheckResourceAttrSet(resourceName, "public"), + resource.TestCheckResourceAttrSet(resourceName, "rdns"), + resource.TestCheckResourceAttrSet(resourceName, "linode_id"), + resource.TestCheckResourceAttrSet(resourceName, "reserved"), + ), + }, + }, + }) +} + +func TestAccDataSource_reservedIPList(t *testing.T) { + t.Parallel() + + resourceName := "data.linode_reserved_ip.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: tmpl.DataList(t), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "reserved_ips.#"), + ), + }, + }, + }) +} diff --git a/linode/networkreservedips/framework_datasource.go b/linode/networkreservedips/framework_datasource.go new file mode 100644 index 000000000..1c862841a --- /dev/null +++ b/linode/networkreservedips/framework_datasource.go @@ -0,0 +1,124 @@ +package networkreservedips + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/linode/linodego" + "github.com/linode/terraform-provider-linode/v2/linode/helper" +) + +func NewDataSource() datasource.DataSource { + return &DataSource{ + BaseDataSource: helper.NewBaseDataSource( + helper.BaseDataSourceConfig{ + Name: "linode_reserved_ip", + Schema: &frameworkDataSourceSchema, + }, + ), + } +} + +type DataSource struct { + helper.BaseDataSource +} + +type DataSourceModel struct { + ID types.String `tfsdk:"id"` + Address types.String `tfsdk:"address"` + Region types.String `tfsdk:"region"` + 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"` + Reserved types.Bool `tfsdk:"reserved"` + ReservedIPs types.List `tfsdk:"reserved_ips"` +} + +func (data *DataSourceModel) parseIP(ip *linodego.InstanceIP) { + data.ID = types.StringValue(ip.Address) + data.Address = types.StringValue(ip.Address) + data.Region = types.StringValue(ip.Region) + data.Gateway = types.StringValue(ip.Gateway) + data.SubnetMask = types.StringValue(ip.SubnetMask) + data.Prefix = types.Int64Value(int64(ip.Prefix)) + data.Type = types.StringValue(string(ip.Type)) + data.Public = types.BoolValue(ip.Public) + data.RDNS = types.StringValue(ip.RDNS) + data.LinodeID = types.Int64Value(int64(ip.LinodeID)) + data.Reserved = types.BoolValue(ip.Reserved) +} + +func (d *DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + tflog.Debug(ctx, "Read data.linode_reserved_ip") + + var data DataSourceModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if !data.Address.IsNull() { + // Fetch a specific reserved IP + ip, err := d.Meta.Client.GetReservedIPAddress(ctx, data.Address.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Unable to get Reserved IP Address", + err.Error(), + ) + return + } + data.parseIP(ip) + } else { + // List all reserved IPs + filter := "" + if !data.Region.IsNull() { + filter = fmt.Sprintf("{\"region\":\"%s\"}", data.Region.ValueString()) + } + ips, err := d.Meta.Client.ListReservedIPAddresses(ctx, &linodego.ListOptions{Filter: filter}) + if err != nil { + resp.Diagnostics.AddError( + "Unable to list Reserved IP Addresses", + err.Error(), + ) + return + } + + reservedIPs := make([]ReservedIPObject, len(ips)) + for i, ip := range ips { + reservedIPs[i] = ReservedIPObject{ + ID: types.StringValue(ip.Address), + Address: types.StringValue(ip.Address), + Region: types.StringValue(ip.Region), + Gateway: types.StringValue(ip.Gateway), + SubnetMask: 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), + LinodeID: types.Int64Value(int64(ip.LinodeID)), + Reserved: types.BoolValue(ip.Reserved), + } + } + + reservedIPsValue, diags := types.ListValueFrom(ctx, reservedIPObjectType, reservedIPs) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + data.ReservedIPs = reservedIPsValue + + // If there are IPs, populate the first one's details for backwards compatibility + if len(ips) > 0 { + data.parseIP(&ips[0]) + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/linode/networkreservedips/framework_datasource_schema.go b/linode/networkreservedips/framework_datasource_schema.go new file mode 100644 index 000000000..20f5099e8 --- /dev/null +++ b/linode/networkreservedips/framework_datasource_schema.go @@ -0,0 +1,91 @@ +package networkreservedips + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type ReservedIPObject struct { + ID types.String `tfsdk:"id"` + Address types.String `tfsdk:"address"` + Region types.String `tfsdk:"region"` + 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"` + Reserved types.Bool `tfsdk:"reserved"` +} + +var reservedIPObjectType = types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "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 frameworkDataSourceSchema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "region": schema.StringAttribute{ + Description: "The Region in which to reserve the IP address.", + Optional: true, + }, + "address": schema.StringAttribute{ + Description: "The reserved IP address.", + 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.", + Computed: true, + }, + "linode_id": schema.Int64Attribute{ + Description: "The ID of the Linode this address currently belongs to.", + Computed: true, + }, + "reserved": schema.BoolAttribute{ + Description: "Whether this IP is reserved or not.", + Computed: true, + }, + "id": schema.StringAttribute{ + Description: "The unique ID of the reserved IP address.", + Computed: true, + }, + "reserved_ips": schema.ListAttribute{ + Description: "A list of all reserved IPs.", + Computed: true, + ElementType: reservedIPObjectType, + }, + }, +} diff --git a/linode/networkreservedips/framework_models.go b/linode/networkreservedips/framework_models.go new file mode 100644 index 000000000..4d6be3808 --- /dev/null +++ b/linode/networkreservedips/framework_models.go @@ -0,0 +1,69 @@ +package networkreservedips + +import ( + "context" + + "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 ReservedIPModel struct { + ID types.String `tfsdk:"id"` + Region types.String `tfsdk:"region"` + 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"` + Reserved types.Bool `tfsdk:"reserved"` +} + +func (m *ReservedIPModel) FlattenReservedIP( + ctx context.Context, + ip linodego.InstanceIP, + preserveKnown bool, +) diag.Diagnostics { + var diags diag.Diagnostics + + m.ID = helper.KeepOrUpdateString(m.ID, ip.Address, preserveKnown) + m.Region = helper.KeepOrUpdateString(m.Region, ip.Region, preserveKnown) + m.Address = helper.KeepOrUpdateString(m.Address, ip.Address, preserveKnown) + m.Gateway = helper.KeepOrUpdateString(m.Gateway, ip.Gateway, preserveKnown) + m.SubnetMask = helper.KeepOrUpdateString(m.SubnetMask, ip.SubnetMask, preserveKnown) + m.Prefix = helper.KeepOrUpdateInt64(m.Prefix, int64(ip.Prefix), preserveKnown) + m.Type = helper.KeepOrUpdateString(m.Type, string(ip.Type), preserveKnown) + m.Public = helper.KeepOrUpdateBool(m.Public, ip.Public, preserveKnown) + m.RDNS = helper.KeepOrUpdateString(m.RDNS, ip.RDNS, preserveKnown) + m.LinodeID = helper.KeepOrUpdateInt64(m.LinodeID, int64(ip.LinodeID), preserveKnown) + m.Reserved = helper.KeepOrUpdateBool(m.Reserved, ip.Reserved, preserveKnown) + + return diags +} + +func (m *ReservedIPModel) CopyFrom( + ctx context.Context, + other ReservedIPModel, + preserveKnown bool, +) diag.Diagnostics { + var diags diag.Diagnostics + + m.ID = helper.KeepOrUpdateValue(m.ID, other.ID, preserveKnown) + m.Region = helper.KeepOrUpdateValue(m.Region, other.Region, preserveKnown) + m.Address = helper.KeepOrUpdateValue(m.Address, other.Address, preserveKnown) + m.Gateway = helper.KeepOrUpdateValue(m.Gateway, other.Gateway, preserveKnown) + m.SubnetMask = helper.KeepOrUpdateValue(m.SubnetMask, other.SubnetMask, preserveKnown) + m.Prefix = helper.KeepOrUpdateValue(m.Prefix, other.Prefix, preserveKnown) + m.Type = helper.KeepOrUpdateValue(m.Type, other.Type, preserveKnown) + m.Public = helper.KeepOrUpdateValue(m.Public, other.Public, preserveKnown) + m.RDNS = helper.KeepOrUpdateValue(m.RDNS, other.RDNS, preserveKnown) + m.LinodeID = helper.KeepOrUpdateValue(m.LinodeID, other.LinodeID, preserveKnown) + m.Reserved = helper.KeepOrUpdateValue(m.Reserved, other.Reserved, preserveKnown) + + return diags +} diff --git a/linode/networkreservedips/framework_resource.go b/linode/networkreservedips/framework_resource.go new file mode 100644 index 000000000..88407eaee --- /dev/null +++ b/linode/networkreservedips/framework_resource.go @@ -0,0 +1,181 @@ +package networkreservedips + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/linode/linodego" + "github.com/linode/terraform-provider-linode/v2/linode/helper" +) + +func NewResource() resource.Resource { + return &Resource{ + BaseResource: helper.NewBaseResource( + helper.BaseResourceConfig{ + Name: "linode_reserved_ip", + IDType: types.StringType, + Schema: &frameworkResourceSchema, + }, + ), + } +} + +type Resource struct { + helper.BaseResource +} + +func (r *Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + tflog.Debug(ctx, "Starting Create for linode_reserved_ip") + var plan ReservedIPModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Error getting plan", map[string]interface{}{ + "error": resp.Diagnostics.Errors(), + }) + return + } + + ctx = populateLogAttributes(ctx, &plan) + + client := r.Meta.Client + reserveIP, err := client.ReserveIPAddress(ctx, linodego.ReserveIPOptions{ + Region: plan.Region.ValueString(), + }) + if err != nil { + tflog.Error(ctx, "Failed to reserve IP address", map[string]interface{}{ + "error": err.Error(), + }) + resp.Diagnostics.AddError( + "Failed to reserve IP address", + err.Error(), + ) + return + } + + if reserveIP == nil { + tflog.Error(ctx, "Received nil pointer for reserved IP") + resp.Diagnostics.AddError("nil Pointer", "received nil pointer of the reserved ip") + return + } + + tflog.Debug(ctx, "Successfully reserved IP address", map[string]interface{}{ + "address": reserveIP.Address, + "region": reserveIP.Region, + }) + + plan.ID = types.StringValue(reserveIP.Address) + tflog.Debug(ctx, "Setting ID for reserved IP", map[string]interface{}{ + "id": plan.ID.ValueString(), + }) + + diags := plan.FlattenReservedIP(ctx, *reserveIP, true) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Error flattening reserved IP", map[string]interface{}{ + "error": resp.Diagnostics.Errors(), + }) + return + } + + diags = resp.State.Set(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + tflog.Error(ctx, "Error setting state for reserved IP", map[string]interface{}{ + "error": resp.Diagnostics.Errors(), + }) + } else { + tflog.Debug(ctx, "Successfully set state for reserved IP", map[string]interface{}{ + "id": plan.ID.ValueString(), + }) + } +} + +func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + tflog.Debug(ctx, "Read linode_reserved_ip") + var state ReservedIPModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if helper.FrameworkAttemptRemoveResourceForEmptyID(ctx, state.ID, resp) { + return + } + + ctx = populateLogAttributes(ctx, &state) + + client := r.Meta.Client + address := state.Address.ValueString() + + reservedIP, err := client.GetReservedIPAddress(ctx, address) + if err != nil { + if lerr, ok := err.(*linodego.Error); ok && lerr.Code == 404 { + resp.Diagnostics.AddWarning( + "Reserved IP No Longer Exists", + fmt.Sprintf( + "Removing reserved IP %s from state because it no longer exists", + state.ID.ValueString(), + ), + ) + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError( + "Unable to Refresh the Reserved IP", + fmt.Sprintf( + "Error finding the specified Reserved IP: %s", + err.Error(), + ), + ) + return + } + + if reservedIP == nil { + resp.Diagnostics.AddError("nil Pointer", "received nil pointer of the reserved ip") + return + } + + resp.Diagnostics.Append(state.FlattenReservedIP(ctx, *reservedIP, false)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // Reserved IPs cannot be updated, so this method is left empty +} + +func (r *Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state ReservedIPModel + + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + client := r.Meta.Client + address := state.Address.ValueString() + + tflog.Debug(ctx, "client.DeleteReservedIPAddress(...)") + if err := client.DeleteReservedIPAddress(ctx, address); err != nil { + if lErr, ok := err.(*linodego.Error); (ok && lErr.Code != 404) || !ok { + resp.Diagnostics.AddError( + "Failed to Delete Reserved IP", + fmt.Sprintf( + "failed to delete reserved ip (%s): %s", + address, err.Error(), + ), + ) + } + } +} + +func populateLogAttributes(ctx context.Context, data *ReservedIPModel) context.Context { + return helper.SetLogFieldBulk(ctx, map[string]any{ + "region": data.Region.ValueString(), + "address": data.ID.ValueString(), + }) +} diff --git a/linode/networkreservedips/framework_schema.go b/linode/networkreservedips/framework_schema.go new file mode 100644 index 000000000..d4e03121b --- /dev/null +++ b/linode/networkreservedips/framework_schema.go @@ -0,0 +1,60 @@ +package networkreservedips + +import ( + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" +) + +var frameworkResourceSchema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "region": schema.StringAttribute{ + Description: "The Region in which to reserve the IP address.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "address": schema.StringAttribute{ + Description: "The reserved 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, + }, + "reserved": schema.BoolAttribute{ + Description: "Whether this IP is reserved or not.", + Computed: true, + }, + "id": schema.StringAttribute{ + Description: "The unique ID of the reserved IP address.", + Computed: true, + }, + }, +} diff --git a/linode/networkreservedips/resource_test.go b/linode/networkreservedips/resource_test.go new file mode 100644 index 000000000..5827d7f33 --- /dev/null +++ b/linode/networkreservedips/resource_test.go @@ -0,0 +1,59 @@ +//go:build integration || instance + +package networkreservedips_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/linode/terraform-provider-linode/v2/linode/acceptance" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips/tmpl" +) + +var testRegion string + +func init() { + region, err := acceptance.GetRandomRegionWithCaps([]string{"linodes"}, "core") + if err != nil { + panic(fmt.Sprintf("Error getting random region: %s", err)) + } + testRegion = region + fmt.Println(testRegion) +} + +func TestAccResource_reserveIP(t *testing.T) { + t.Parallel() + + resName := "linode_reserved_ip.test" + instanceName := acctest.RandomWithPrefix("tf_test") + + t.Logf("Starting TestAccResource_reserveIP with resName: %s, instanceName: %s, region: %s", resName, instanceName, testRegion) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + t.Log("Running PreCheck") + acceptance.PreCheck(t) + }, + ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, + CheckDestroy: acceptance.CheckInstanceDestroy, + Steps: []resource.TestStep{ + { + Config: tmpl.ReserveIP(t, instanceName, testRegion), + Check: resource.ComposeTestCheckFunc( + func(s *terraform.State) error { + t.Log("Running Check function") + return nil + }, + resource.TestCheckResourceAttrSet(resName, "id"), + resource.TestCheckResourceAttrSet(resName, "address"), + resource.TestCheckResourceAttr(resName, "region", testRegion), + ), + }, + }, + }) + + t.Log("Finished TestAccResource_reserveIP") +} diff --git a/linode/networkreservedips/tmpl/reserved_ip.gotf b/linode/networkreservedips/tmpl/reserved_ip.gotf new file mode 100644 index 000000000..cd54c26e4 --- /dev/null +++ b/linode/networkreservedips/tmpl/reserved_ip.gotf @@ -0,0 +1,17 @@ +{{ define "reserved_ip_data_basic" }} + +resource "linode_reserved_ip" "test" { + region = "{{ .Region }}" +} + +data "linode_reserved_ip" "test" { + address = linode_reserved_ip.test.address +} + +{{ end }} + +{{ define "reserved_ip_data_list" }} + +data "linode_reserved_ip" "test" {} + +{{ end }} diff --git a/linode/networkreservedips/tmpl/reserved_ip_basic.gotf b/linode/networkreservedips/tmpl/reserved_ip_basic.gotf new file mode 100644 index 000000000..23e617dcf --- /dev/null +++ b/linode/networkreservedips/tmpl/reserved_ip_basic.gotf @@ -0,0 +1,7 @@ +{{ define "reserved_ip_basic" }} + +resource "linode_reserved_ip" "test" { + region = "{{ .Region }}" +} + +{{ end }} \ No newline at end of file diff --git a/linode/networkreservedips/tmpl/template.go b/linode/networkreservedips/tmpl/template.go new file mode 100644 index 000000000..d2cc09e33 --- /dev/null +++ b/linode/networkreservedips/tmpl/template.go @@ -0,0 +1,31 @@ +package tmpl + +import ( + "testing" + + "github.com/linode/terraform-provider-linode/v2/linode/acceptance" +) + +type TemplateData struct { + Region string + Address string +} + +func DataBasic(t *testing.T, region string) string { + return acceptance.ExecuteTemplate(t, + "reserved_ip_data_basic", TemplateData{ + Region: region, + }) +} + +func DataList(t *testing.T) string { + return acceptance.ExecuteTemplate(t, "reserved_ip_data_list", nil) +} + +// ReserveIP generates the Terraform configuration for reserving an IP address +func ReserveIP(t *testing.T, name, region string) string { + return acceptance.ExecuteTemplate(t, + "reserved_ip_basic", TemplateData{ + Region: region, + }) +} From c9196b8b0a6bea95a48103d9977251deaeb07221 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Thu, 26 Sep 2024 13:59:00 -0400 Subject: [PATCH 3/7] temporarily changed linodego to point to the latest commit on main branch before next release --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9367812a1..709ffe91f 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 - github.com/linode/linodego v1.40.0 + github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0 github.com/linode/linodego/k8s v1.25.2 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.27.0 @@ -101,7 +101,7 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.15.0 // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.24.0 // indirect golang.org/x/text v0.18.0 // indirect diff --git a/go.sum b/go.sum index 8210e3d73..d8004321b 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linode/linodego v1.40.0 h1:7ESY0PwK94hoggoCtIroT1Xk6b1flrFBNZ6KwqbTqlI= -github.com/linode/linodego v1.40.0/go.mod h1:NsUw4l8QrLdIofRg1NYFBbW5ZERnmbZykVBszPZLORM= +github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0 h1:FbA+CGk47kdAm2XmVEm1rVCLFlo98uJ+5jnbTLjVkv8= +github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY= github.com/linode/linodego/k8s v1.25.2 h1:PY6S0sAD3xANVvM9WY38bz9GqMTjIbytC8IJJ9Cv23o= github.com/linode/linodego/k8s v1.25.2/go.mod h1:DC1XCSRZRGsmaa/ggpDPSDUmOM6aK1bhSIP6+f9Cwhc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= @@ -298,8 +298,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From b6638d8b43fdf3e8e699bc9b79d26dfaf2ce74c3 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Thu, 26 Sep 2024 17:11:51 -0400 Subject: [PATCH 4/7] added go buil inegration || networkreservedips to prevent integration tests from being run by unit test targets --- linode/networkreservedips/datasource_test.go | 2 ++ linode/networkreservedips/resource_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/linode/networkreservedips/datasource_test.go b/linode/networkreservedips/datasource_test.go index 0826e3663..49b461443 100644 --- a/linode/networkreservedips/datasource_test.go +++ b/linode/networkreservedips/datasource_test.go @@ -1,3 +1,5 @@ +//go:build integration || networkreservedips + package networkreservedips_test import ( diff --git a/linode/networkreservedips/resource_test.go b/linode/networkreservedips/resource_test.go index 5827d7f33..5b4307e66 100644 --- a/linode/networkreservedips/resource_test.go +++ b/linode/networkreservedips/resource_test.go @@ -1,4 +1,4 @@ -//go:build integration || instance +//go:build integration || networkreservedips package networkreservedips_test From be57ac73b2b06cb6863444d482dd14aa2e168de9 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Tue, 8 Oct 2024 11:20:47 -0400 Subject: [PATCH 5/7] removed unused import statement and changed linodego version to point to the latest release --- go.mod | 2 +- go.sum | 4 ++-- linode/framework_provider.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 5a510ea2d..9a23c4b9f 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 github.com/hashicorp/terraform-plugin-testing v1.10.0 - github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0 + github.com/linode/linodego v1.41.0 github.com/linode/linodego/k8s v1.25.2 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.28.0 diff --git a/go.sum b/go.sum index d9a52a6de..a1f50f3e6 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0 h1:FbA+CGk47kdAm2XmVEm1rVCLFlo98uJ+5jnbTLjVkv8= -github.com/linode/linodego v1.41.1-0.20240925173015-b20be2e986e0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY= +github.com/linode/linodego v1.41.0 h1:GcP7JIBr9iLRJ9FwAtb9/WCT1DuPJS/xUApapfdjtiY= +github.com/linode/linodego v1.41.0/go.mod h1:Ow4/XZ0yvWBzt3iAHwchvhSx30AyLintsSMvvQ2/SJY= github.com/linode/linodego/k8s v1.25.2 h1:PY6S0sAD3xANVvM9WY38bz9GqMTjIbytC8IJJ9Cv23o= github.com/linode/linodego/k8s v1.25.2/go.mod h1:DC1XCSRZRGsmaa/ggpDPSDUmOM6aK1bhSIP6+f9Cwhc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= diff --git a/linode/framework_provider.go b/linode/framework_provider.go index 718213eed..e52acd9f0 100644 --- a/linode/framework_provider.go +++ b/linode/framework_provider.go @@ -2,8 +2,7 @@ package linode import ( "context" - "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips" - "github.com/linode/terraform-provider-linode/v2/linode/vpcips" + "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/schema" @@ -54,6 +53,7 @@ import ( "github.com/linode/terraform-provider-linode/v2/linode/nbs" "github.com/linode/terraform-provider-linode/v2/linode/nbtypes" "github.com/linode/terraform-provider-linode/v2/linode/networkingip" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips" "github.com/linode/terraform-provider-linode/v2/linode/networktransferprices" "github.com/linode/terraform-provider-linode/v2/linode/objbucket" "github.com/linode/terraform-provider-linode/v2/linode/objcluster" From 38195dfed7703ea2bc737af6ace42370cba4d9d4 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Mon, 14 Oct 2024 12:35:48 -0400 Subject: [PATCH 6/7] removed unnecessary region filter from datasource --- linode/networkreservedips/framework_datasource.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linode/networkreservedips/framework_datasource.go b/linode/networkreservedips/framework_datasource.go index 1c862841a..86e097ee1 100644 --- a/linode/networkreservedips/framework_datasource.go +++ b/linode/networkreservedips/framework_datasource.go @@ -2,7 +2,6 @@ package networkreservedips import ( "context" - "fmt" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/types" @@ -78,9 +77,6 @@ func (d *DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp } else { // List all reserved IPs filter := "" - if !data.Region.IsNull() { - filter = fmt.Sprintf("{\"region\":\"%s\"}", data.Region.ValueString()) - } ips, err := d.Meta.Client.ListReservedIPAddresses(ctx, &linodego.ListOptions{Filter: filter}) if err != nil { resp.Diagnostics.AddError( From 24493f8ed55b185f42234a06aec690c315f9f014 Mon Sep 17 00:00:00 2001 From: Anirudh Jagadish Date: Wed, 16 Oct 2024 04:07:48 -0400 Subject: [PATCH 7/7] Created a separate datasource for listing reserved IPs, added the corresponding test. Removed unnessary logs --- linode/framework_provider.go | 6 +- linode/image/resource_test.go | 6 +- .../datasource_fetch_model.go | 56 ++++++++ .../datasource_fetch_schema.go} | 10 +- .../datasource_test.go | 27 +--- .../framework_datasource_fetch.go | 90 +++++++++++++ .../framework_models.go | 40 +++--- .../framework_resource.go | 68 ++++------ .../framework_schema.go | 2 +- .../resource_test.go | 11 +- .../tmpl/reserved_ip.gotf | 7 +- .../tmpl/reserved_ip_basic.gotf | 0 linode/networkreservedip/tmpl/template.go | 27 ++++ .../datasource_list_model.go | 9 ++ .../datasource_list_schema.go | 47 +++++++ .../datasource_list_test.go | 30 +++++ .../framework_datasource.go | 120 ------------------ .../framework_datasource_list.go | 72 +++++++++++ .../tmpl/reserved_ip_list.gotf | 9 ++ linode/networkreservedips/tmpl/template.go | 20 --- 20 files changed, 403 insertions(+), 254 deletions(-) create mode 100644 linode/networkreservedip/datasource_fetch_model.go rename linode/{networkreservedips/framework_datasource_schema.go => networkreservedip/datasource_fetch_schema.go} (92%) rename linode/{networkreservedips => networkreservedip}/datasource_test.go (67%) create mode 100644 linode/networkreservedip/framework_datasource_fetch.go rename linode/{networkreservedips => networkreservedip}/framework_models.go (62%) rename linode/{networkreservedips => networkreservedip}/framework_resource.go (65%) rename linode/{networkreservedips => networkreservedip}/framework_schema.go (98%) rename linode/{networkreservedips => networkreservedip}/resource_test.go (82%) rename linode/{networkreservedips => networkreservedip}/tmpl/reserved_ip.gotf (58%) rename linode/{networkreservedips => networkreservedip}/tmpl/reserved_ip_basic.gotf (100%) create mode 100644 linode/networkreservedip/tmpl/template.go create mode 100644 linode/networkreservedips/datasource_list_model.go create mode 100644 linode/networkreservedips/datasource_list_schema.go create mode 100644 linode/networkreservedips/datasource_list_test.go delete mode 100644 linode/networkreservedips/framework_datasource.go create mode 100644 linode/networkreservedips/framework_datasource_list.go create mode 100644 linode/networkreservedips/tmpl/reserved_ip_list.gotf diff --git a/linode/framework_provider.go b/linode/framework_provider.go index e52acd9f0..25d5f03a4 100644 --- a/linode/framework_provider.go +++ b/linode/framework_provider.go @@ -53,6 +53,7 @@ import ( "github.com/linode/terraform-provider-linode/v2/linode/nbs" "github.com/linode/terraform-provider-linode/v2/linode/nbtypes" "github.com/linode/terraform-provider-linode/v2/linode/networkingip" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedip" "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips" "github.com/linode/terraform-provider-linode/v2/linode/networktransferprices" "github.com/linode/terraform-provider-linode/v2/linode/objbucket" @@ -226,7 +227,7 @@ func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Res firewall.NewResource, placementgroup.NewResource, placementgroupassignment.NewResource, - networkreservedips.NewResource, + networkreservedip.NewResource, } } @@ -295,6 +296,7 @@ func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource placementgroups.NewDataSource, childaccount.NewDataSource, childaccounts.NewDataSource, - networkreservedips.NewDataSource, + networkreservedip.NewDataSourceFetch, + networkreservedips.NewDataSourceList, } } diff --git a/linode/image/resource_test.go b/linode/image/resource_test.go index 8184c5d7b..f40af614b 100644 --- a/linode/image/resource_test.go +++ b/linode/image/resource_test.go @@ -36,8 +36,10 @@ var testImageBytesNew = []byte{ 0xe4, 0x02, 0x00, 0x7a, 0x7a, 0x6f, 0xed, 0x03, 0x00, 0x00, 0x00, } -var testRegion string -var testRegions []string +var ( + testRegion string + testRegions []string +) func init() { resource.AddTestSweepers("linode_image", &resource.Sweeper{ diff --git a/linode/networkreservedip/datasource_fetch_model.go b/linode/networkreservedip/datasource_fetch_model.go new file mode 100644 index 000000000..cb6d9197e --- /dev/null +++ b/linode/networkreservedip/datasource_fetch_model.go @@ -0,0 +1,56 @@ +package networkreservedip + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/linode/linodego" +) + +type DataSourceFetchModel struct { + ID types.String `tfsdk:"id"` + Address types.String `tfsdk:"address"` + Region types.String `tfsdk:"region"` + 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"` + Reserved types.Bool `tfsdk:"reserved"` +} + +func (data *DataSourceFetchModel) parseIP(ip *linodego.InstanceIP) { + data.ID = types.StringValue(ip.Address) + data.Address = types.StringValue(ip.Address) + data.Region = types.StringValue(ip.Region) + data.Gateway = types.StringValue(ip.Gateway) + data.SubnetMask = types.StringValue(ip.SubnetMask) + data.Prefix = types.Int64Value(int64(ip.Prefix)) + data.Type = types.StringValue(string(ip.Type)) + data.Public = types.BoolValue(ip.Public) + data.RDNS = types.StringValue(ip.RDNS) + data.LinodeID = types.Int64Value(int64(ip.LinodeID)) + data.Reserved = types.BoolValue(ip.Reserved) +} + +// func (m *ReservedIPModel) CopyFrom( +// ctx context.Context, +// other ReservedIPModel, +// preserveKnown bool, +// ) diag.Diagnostics { +// var diags diag.Diagnostics + +// m.ID = helper.KeepOrUpdateValue(m.ID, other.ID, preserveKnown) +// m.Region = helper.KeepOrUpdateValue(m.Region, other.Region, preserveKnown) +// m.Address = helper.KeepOrUpdateValue(m.Address, other.Address, preserveKnown) +// m.Gateway = helper.KeepOrUpdateValue(m.Gateway, other.Gateway, preserveKnown) +// m.SubnetMask = helper.KeepOrUpdateValue(m.SubnetMask, other.SubnetMask, preserveKnown) +// m.Prefix = helper.KeepOrUpdateValue(m.Prefix, other.Prefix, preserveKnown) +// m.Type = helper.KeepOrUpdateValue(m.Type, other.Type, preserveKnown) +// m.Public = helper.KeepOrUpdateValue(m.Public, other.Public, preserveKnown) +// m.RDNS = helper.KeepOrUpdateValue(m.RDNS, other.RDNS, preserveKnown) +// m.LinodeID = helper.KeepOrUpdateValue(m.LinodeID, other.LinodeID, preserveKnown) +// m.Reserved = helper.KeepOrUpdateValue(m.Reserved, other.Reserved, preserveKnown) + +// return diags +// } diff --git a/linode/networkreservedips/framework_datasource_schema.go b/linode/networkreservedip/datasource_fetch_schema.go similarity index 92% rename from linode/networkreservedips/framework_datasource_schema.go rename to linode/networkreservedip/datasource_fetch_schema.go index 20f5099e8..efc1e58dd 100644 --- a/linode/networkreservedips/framework_datasource_schema.go +++ b/linode/networkreservedip/datasource_fetch_schema.go @@ -1,4 +1,4 @@ -package networkreservedips +package networkreservedip import ( "github.com/hashicorp/terraform-plugin-framework/attr" @@ -36,7 +36,7 @@ var reservedIPObjectType = types.ObjectType{ }, } -var frameworkDataSourceSchema = schema.Schema{ +var frameworkDataSourceFetchSchema = schema.Schema{ Attributes: map[string]schema.Attribute{ "region": schema.StringAttribute{ Description: "The Region in which to reserve the IP address.", @@ -44,6 +44,7 @@ var frameworkDataSourceSchema = schema.Schema{ }, "address": schema.StringAttribute{ Description: "The reserved IP address.", + Computed: true, Optional: true, }, "gateway": schema.StringAttribute{ @@ -82,10 +83,5 @@ var frameworkDataSourceSchema = schema.Schema{ Description: "The unique ID of the reserved IP address.", Computed: true, }, - "reserved_ips": schema.ListAttribute{ - Description: "A list of all reserved IPs.", - Computed: true, - ElementType: reservedIPObjectType, - }, }, } diff --git a/linode/networkreservedips/datasource_test.go b/linode/networkreservedip/datasource_test.go similarity index 67% rename from linode/networkreservedips/datasource_test.go rename to linode/networkreservedip/datasource_test.go index 49b461443..5520d9fa0 100644 --- a/linode/networkreservedips/datasource_test.go +++ b/linode/networkreservedip/datasource_test.go @@ -1,19 +1,19 @@ -//go:build integration || networkreservedips +//go:build integration || networkreservedip -package networkreservedips_test +package networkreservedip_test import ( "testing" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/linode/terraform-provider-linode/v2/linode/acceptance" - "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips/tmpl" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedip/tmpl" ) func TestAccDataSource_reservedIP(t *testing.T) { t.Parallel() - resourceName := "data.linode_reserved_ip.test" + resourceName := "data.linode_reserved_ip_fetch.test" region, _ := acceptance.GetRandomRegionWithCaps([]string{"linodes"}, "core") resource.Test(t, resource.TestCase{ @@ -38,22 +38,3 @@ func TestAccDataSource_reservedIP(t *testing.T) { }, }) } - -func TestAccDataSource_reservedIPList(t *testing.T) { - t.Parallel() - - resourceName := "data.linode_reserved_ip.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acceptance.PreCheck(t) }, - ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, - Steps: []resource.TestStep{ - { - Config: tmpl.DataList(t), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceName, "reserved_ips.#"), - ), - }, - }, - }) -} diff --git a/linode/networkreservedip/framework_datasource_fetch.go b/linode/networkreservedip/framework_datasource_fetch.go new file mode 100644 index 000000000..59ff5f163 --- /dev/null +++ b/linode/networkreservedip/framework_datasource_fetch.go @@ -0,0 +1,90 @@ +package networkreservedip + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/linode/terraform-provider-linode/v2/linode/helper" +) + +func NewDataSourceFetch() datasource.DataSource { + return &DataSource{ + BaseDataSource: helper.NewBaseDataSource( + helper.BaseDataSourceConfig{ + Name: "linode_reserved_ip_fetch", + Schema: &frameworkDataSourceFetchSchema, + }, + ), + } +} + +type DataSource struct { + helper.BaseDataSource +} + +func (d *DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + tflog.Debug(ctx, "Read data.linode_reserved_ip") + + var data DataSourceFetchModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + if !data.Address.IsNull() { + // Fetch a specific reserved IP + ip, err := d.Meta.Client.GetReservedIPAddress(ctx, data.Address.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Unable to get Reserved IP Address", + err.Error(), + ) + return + } + data.parseIP(ip) + } + // } else { + // // List all reserved IPs + // filter := "" + // ips, err := d.Meta.Client.ListReservedIPAddresses(ctx, &linodego.ListOptions{Filter: filter}) + // if err != nil { + // resp.Diagnostics.AddError( + // "Unable to list Reserved IP Addresses", + // err.Error(), + // ) + // return + // } + + // reservedIPs := make([]ReservedIPObject, len(ips)) + // for i, ip := range ips { + // reservedIPs[i] = ReservedIPObject{ + // ID: types.StringValue(ip.Address), + // Address: types.StringValue(ip.Address), + // Region: types.StringValue(ip.Region), + // Gateway: types.StringValue(ip.Gateway), + // SubnetMask: 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), + // LinodeID: types.Int64Value(int64(ip.LinodeID)), + // Reserved: types.BoolValue(ip.Reserved), + // } + // } + + // reservedIPsValue, diags := types.ListValueFrom(ctx, reservedIPObjectType, reservedIPs) + // resp.Diagnostics.Append(diags...) + // if resp.Diagnostics.HasError() { + // return + // } + // data.ReservedIPs = reservedIPsValue + + // // If there are IPs, populate the first one's details for backwards compatibility + // if len(ips) > 0 { + // data.parseIP(&ips[0]) + // } + // } + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/linode/networkreservedips/framework_models.go b/linode/networkreservedip/framework_models.go similarity index 62% rename from linode/networkreservedips/framework_models.go rename to linode/networkreservedip/framework_models.go index 4d6be3808..9d4accabc 100644 --- a/linode/networkreservedips/framework_models.go +++ b/linode/networkreservedip/framework_models.go @@ -1,4 +1,4 @@ -package networkreservedips +package networkreservedip import ( "context" @@ -46,24 +46,24 @@ func (m *ReservedIPModel) FlattenReservedIP( return diags } -func (m *ReservedIPModel) CopyFrom( - ctx context.Context, - other ReservedIPModel, - preserveKnown bool, -) diag.Diagnostics { - var diags diag.Diagnostics +// func (m *ReservedIPModel) CopyFrom( +// ctx context.Context, +// other ReservedIPModel, +// preserveKnown bool, +// ) diag.Diagnostics { +// var diags diag.Diagnostics - m.ID = helper.KeepOrUpdateValue(m.ID, other.ID, preserveKnown) - m.Region = helper.KeepOrUpdateValue(m.Region, other.Region, preserveKnown) - m.Address = helper.KeepOrUpdateValue(m.Address, other.Address, preserveKnown) - m.Gateway = helper.KeepOrUpdateValue(m.Gateway, other.Gateway, preserveKnown) - m.SubnetMask = helper.KeepOrUpdateValue(m.SubnetMask, other.SubnetMask, preserveKnown) - m.Prefix = helper.KeepOrUpdateValue(m.Prefix, other.Prefix, preserveKnown) - m.Type = helper.KeepOrUpdateValue(m.Type, other.Type, preserveKnown) - m.Public = helper.KeepOrUpdateValue(m.Public, other.Public, preserveKnown) - m.RDNS = helper.KeepOrUpdateValue(m.RDNS, other.RDNS, preserveKnown) - m.LinodeID = helper.KeepOrUpdateValue(m.LinodeID, other.LinodeID, preserveKnown) - m.Reserved = helper.KeepOrUpdateValue(m.Reserved, other.Reserved, preserveKnown) +// m.ID = helper.KeepOrUpdateValue(m.ID, other.ID, preserveKnown) +// m.Region = helper.KeepOrUpdateValue(m.Region, other.Region, preserveKnown) +// m.Address = helper.KeepOrUpdateValue(m.Address, other.Address, preserveKnown) +// m.Gateway = helper.KeepOrUpdateValue(m.Gateway, other.Gateway, preserveKnown) +// m.SubnetMask = helper.KeepOrUpdateValue(m.SubnetMask, other.SubnetMask, preserveKnown) +// m.Prefix = helper.KeepOrUpdateValue(m.Prefix, other.Prefix, preserveKnown) +// m.Type = helper.KeepOrUpdateValue(m.Type, other.Type, preserveKnown) +// m.Public = helper.KeepOrUpdateValue(m.Public, other.Public, preserveKnown) +// m.RDNS = helper.KeepOrUpdateValue(m.RDNS, other.RDNS, preserveKnown) +// m.LinodeID = helper.KeepOrUpdateValue(m.LinodeID, other.LinodeID, preserveKnown) +// m.Reserved = helper.KeepOrUpdateValue(m.Reserved, other.Reserved, preserveKnown) - return diags -} +// return diags +// } diff --git a/linode/networkreservedips/framework_resource.go b/linode/networkreservedip/framework_resource.go similarity index 65% rename from linode/networkreservedips/framework_resource.go rename to linode/networkreservedip/framework_resource.go index 88407eaee..cf85923e6 100644 --- a/linode/networkreservedips/framework_resource.go +++ b/linode/networkreservedip/framework_resource.go @@ -1,4 +1,4 @@ -package networkreservedips +package networkreservedip import ( "context" @@ -29,26 +29,20 @@ type Resource struct { func (r *Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { tflog.Debug(ctx, "Starting Create for linode_reserved_ip") - var plan ReservedIPModel + var data ReservedIPModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { - tflog.Error(ctx, "Error getting plan", map[string]interface{}{ - "error": resp.Diagnostics.Errors(), - }) return } - ctx = populateLogAttributes(ctx, &plan) + ctx = populateLogAttributes(ctx, &data) client := r.Meta.Client reserveIP, err := client.ReserveIPAddress(ctx, linodego.ReserveIPOptions{ - Region: plan.Region.ValueString(), + Region: data.Region.ValueString(), }) if err != nil { - tflog.Error(ctx, "Failed to reserve IP address", map[string]interface{}{ - "error": err.Error(), - }) resp.Diagnostics.AddError( "Failed to reserve IP address", err.Error(), @@ -57,60 +51,41 @@ func (r *Resource) Create(ctx context.Context, req resource.CreateRequest, resp } if reserveIP == nil { - tflog.Error(ctx, "Received nil pointer for reserved IP") resp.Diagnostics.AddError("nil Pointer", "received nil pointer of the reserved ip") return } - tflog.Debug(ctx, "Successfully reserved IP address", map[string]interface{}{ - "address": reserveIP.Address, - "region": reserveIP.Region, - }) - - plan.ID = types.StringValue(reserveIP.Address) + data.ID = types.StringValue(reserveIP.Address) tflog.Debug(ctx, "Setting ID for reserved IP", map[string]interface{}{ - "id": plan.ID.ValueString(), + "id": data.ID.ValueString(), }) - diags := plan.FlattenReservedIP(ctx, *reserveIP, true) + diags := data.FlattenReservedIP(ctx, *reserveIP, true) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { - tflog.Error(ctx, "Error flattening reserved IP", map[string]interface{}{ - "error": resp.Diagnostics.Errors(), - }) return } - diags = resp.State.Set(ctx, &plan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - tflog.Error(ctx, "Error setting state for reserved IP", map[string]interface{}{ - "error": resp.Diagnostics.Errors(), - }) - } else { - tflog.Debug(ctx, "Successfully set state for reserved IP", map[string]interface{}{ - "id": plan.ID.ValueString(), - }) - } + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { tflog.Debug(ctx, "Read linode_reserved_ip") - var state ReservedIPModel + var data ReservedIPModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - if helper.FrameworkAttemptRemoveResourceForEmptyID(ctx, state.ID, resp) { + if helper.FrameworkAttemptRemoveResourceForEmptyID(ctx, data.ID, resp) { return } - ctx = populateLogAttributes(ctx, &state) + ctx = populateLogAttributes(ctx, &data) client := r.Meta.Client - address := state.Address.ValueString() + address := data.Address.ValueString() reservedIP, err := client.GetReservedIPAddress(ctx, address) if err != nil { @@ -119,7 +94,7 @@ func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *res "Reserved IP No Longer Exists", fmt.Sprintf( "Removing reserved IP %s from state because it no longer exists", - state.ID.ValueString(), + data.ID.ValueString(), ), ) resp.State.RemoveResource(ctx) @@ -135,13 +110,16 @@ func (r *Resource) Read(ctx context.Context, req resource.ReadRequest, resp *res return } - if reservedIP == nil { - resp.Diagnostics.AddError("nil Pointer", "received nil pointer of the reserved ip") + // if reservedIP == nil { + // resp.Diagnostics.AddError("nil Pointer", "received nil pointer of the reserved ip") + // return + // } + + resp.Diagnostics.Append(data.FlattenReservedIP(ctx, *reservedIP, false)...) + if resp.Diagnostics.HasError() { return } - - resp.Diagnostics.Append(state.FlattenReservedIP(ctx, *reservedIP, false)...) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } func (r *Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { diff --git a/linode/networkreservedips/framework_schema.go b/linode/networkreservedip/framework_schema.go similarity index 98% rename from linode/networkreservedips/framework_schema.go rename to linode/networkreservedip/framework_schema.go index d4e03121b..7ab010669 100644 --- a/linode/networkreservedips/framework_schema.go +++ b/linode/networkreservedip/framework_schema.go @@ -1,4 +1,4 @@ -package networkreservedips +package networkreservedip import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" diff --git a/linode/networkreservedips/resource_test.go b/linode/networkreservedip/resource_test.go similarity index 82% rename from linode/networkreservedips/resource_test.go rename to linode/networkreservedip/resource_test.go index 5b4307e66..3ec18081b 100644 --- a/linode/networkreservedips/resource_test.go +++ b/linode/networkreservedip/resource_test.go @@ -1,6 +1,6 @@ -//go:build integration || networkreservedips +//go:build integration || networkreservedip -package networkreservedips_test +package networkreservedip_test import ( "fmt" @@ -10,7 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/linode/terraform-provider-linode/v2/linode/acceptance" - "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips/tmpl" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedip/tmpl" ) var testRegion string @@ -29,12 +29,8 @@ func TestAccResource_reserveIP(t *testing.T) { resName := "linode_reserved_ip.test" instanceName := acctest.RandomWithPrefix("tf_test") - - t.Logf("Starting TestAccResource_reserveIP with resName: %s, instanceName: %s, region: %s", resName, instanceName, testRegion) - resource.Test(t, resource.TestCase{ PreCheck: func() { - t.Log("Running PreCheck") acceptance.PreCheck(t) }, ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, @@ -44,7 +40,6 @@ func TestAccResource_reserveIP(t *testing.T) { Config: tmpl.ReserveIP(t, instanceName, testRegion), Check: resource.ComposeTestCheckFunc( func(s *terraform.State) error { - t.Log("Running Check function") return nil }, resource.TestCheckResourceAttrSet(resName, "id"), diff --git a/linode/networkreservedips/tmpl/reserved_ip.gotf b/linode/networkreservedip/tmpl/reserved_ip.gotf similarity index 58% rename from linode/networkreservedips/tmpl/reserved_ip.gotf rename to linode/networkreservedip/tmpl/reserved_ip.gotf index cd54c26e4..efc2ff330 100644 --- a/linode/networkreservedips/tmpl/reserved_ip.gotf +++ b/linode/networkreservedip/tmpl/reserved_ip.gotf @@ -4,14 +4,9 @@ resource "linode_reserved_ip" "test" { region = "{{ .Region }}" } -data "linode_reserved_ip" "test" { +data "linode_reserved_ip_fetch" "test" { address = linode_reserved_ip.test.address } {{ end }} -{{ define "reserved_ip_data_list" }} - -data "linode_reserved_ip" "test" {} - -{{ end }} diff --git a/linode/networkreservedips/tmpl/reserved_ip_basic.gotf b/linode/networkreservedip/tmpl/reserved_ip_basic.gotf similarity index 100% rename from linode/networkreservedips/tmpl/reserved_ip_basic.gotf rename to linode/networkreservedip/tmpl/reserved_ip_basic.gotf diff --git a/linode/networkreservedip/tmpl/template.go b/linode/networkreservedip/tmpl/template.go new file mode 100644 index 000000000..80f1f70d9 --- /dev/null +++ b/linode/networkreservedip/tmpl/template.go @@ -0,0 +1,27 @@ +package tmpl + +import ( + "testing" + + "github.com/linode/terraform-provider-linode/v2/linode/acceptance" +) + +type TemplateData struct { + Region string + Address string +} + +func DataBasic(t *testing.T, region string) string { + return acceptance.ExecuteTemplate(t, + "reserved_ip_data_basic", TemplateData{ + Region: region, + }) +} + +// ReserveIP generates the Terraform configuration for reserving an IP address +func ReserveIP(t *testing.T, name, region string) string { + return acceptance.ExecuteTemplate(t, + "reserved_ip_basic", TemplateData{ + Region: region, + }) +} diff --git a/linode/networkreservedips/datasource_list_model.go b/linode/networkreservedips/datasource_list_model.go new file mode 100644 index 000000000..79656ad51 --- /dev/null +++ b/linode/networkreservedips/datasource_list_model.go @@ -0,0 +1,9 @@ +package networkreservedips + +import ( + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type DataSourceListModel struct { + ReservedIPs types.List `tfsdk:"reserved_ips"` +} diff --git a/linode/networkreservedips/datasource_list_schema.go b/linode/networkreservedips/datasource_list_schema.go new file mode 100644 index 000000000..7d8d38d81 --- /dev/null +++ b/linode/networkreservedips/datasource_list_schema.go @@ -0,0 +1,47 @@ +package networkreservedips + +import ( + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type ReservedIPObject struct { + ID types.String `tfsdk:"id"` + Address types.String `tfsdk:"address"` + Region types.String `tfsdk:"region"` + 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"` + Reserved types.Bool `tfsdk:"reserved"` +} + +var reservedIPObjectType = types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "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 frameworkDataSourceListSchema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "reserved_ips": schema.ListAttribute{ + Description: "A list of all reserved IPs.", + Computed: true, + ElementType: reservedIPObjectType, + }, + }, +} diff --git a/linode/networkreservedips/datasource_list_test.go b/linode/networkreservedips/datasource_list_test.go new file mode 100644 index 000000000..97cfe8052 --- /dev/null +++ b/linode/networkreservedips/datasource_list_test.go @@ -0,0 +1,30 @@ +//go:build integration || networkreservedips + +package networkreservedips_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/linode/terraform-provider-linode/v2/linode/acceptance" + "github.com/linode/terraform-provider-linode/v2/linode/networkreservedips/tmpl" +) + +func TestAccDataSource_reservedIPList(t *testing.T) { + t.Parallel() + + resourceName := "data.linode_reserved_ip_list.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + ProtoV5ProviderFactories: acceptance.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: tmpl.DataList(t), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "reserved_ips.#"), + ), + }, + }, + }) +} diff --git a/linode/networkreservedips/framework_datasource.go b/linode/networkreservedips/framework_datasource.go deleted file mode 100644 index 86e097ee1..000000000 --- a/linode/networkreservedips/framework_datasource.go +++ /dev/null @@ -1,120 +0,0 @@ -package networkreservedips - -import ( - "context" - - "github.com/hashicorp/terraform-plugin-framework/datasource" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/linode/linodego" - "github.com/linode/terraform-provider-linode/v2/linode/helper" -) - -func NewDataSource() datasource.DataSource { - return &DataSource{ - BaseDataSource: helper.NewBaseDataSource( - helper.BaseDataSourceConfig{ - Name: "linode_reserved_ip", - Schema: &frameworkDataSourceSchema, - }, - ), - } -} - -type DataSource struct { - helper.BaseDataSource -} - -type DataSourceModel struct { - ID types.String `tfsdk:"id"` - Address types.String `tfsdk:"address"` - Region types.String `tfsdk:"region"` - 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"` - Reserved types.Bool `tfsdk:"reserved"` - ReservedIPs types.List `tfsdk:"reserved_ips"` -} - -func (data *DataSourceModel) parseIP(ip *linodego.InstanceIP) { - data.ID = types.StringValue(ip.Address) - data.Address = types.StringValue(ip.Address) - data.Region = types.StringValue(ip.Region) - data.Gateway = types.StringValue(ip.Gateway) - data.SubnetMask = types.StringValue(ip.SubnetMask) - data.Prefix = types.Int64Value(int64(ip.Prefix)) - data.Type = types.StringValue(string(ip.Type)) - data.Public = types.BoolValue(ip.Public) - data.RDNS = types.StringValue(ip.RDNS) - data.LinodeID = types.Int64Value(int64(ip.LinodeID)) - data.Reserved = types.BoolValue(ip.Reserved) -} - -func (d *DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - tflog.Debug(ctx, "Read data.linode_reserved_ip") - - var data DataSourceModel - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } - - if !data.Address.IsNull() { - // Fetch a specific reserved IP - ip, err := d.Meta.Client.GetReservedIPAddress(ctx, data.Address.ValueString()) - if err != nil { - resp.Diagnostics.AddError( - "Unable to get Reserved IP Address", - err.Error(), - ) - return - } - data.parseIP(ip) - } else { - // List all reserved IPs - filter := "" - ips, err := d.Meta.Client.ListReservedIPAddresses(ctx, &linodego.ListOptions{Filter: filter}) - if err != nil { - resp.Diagnostics.AddError( - "Unable to list Reserved IP Addresses", - err.Error(), - ) - return - } - - reservedIPs := make([]ReservedIPObject, len(ips)) - for i, ip := range ips { - reservedIPs[i] = ReservedIPObject{ - ID: types.StringValue(ip.Address), - Address: types.StringValue(ip.Address), - Region: types.StringValue(ip.Region), - Gateway: types.StringValue(ip.Gateway), - SubnetMask: 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), - LinodeID: types.Int64Value(int64(ip.LinodeID)), - Reserved: types.BoolValue(ip.Reserved), - } - } - - reservedIPsValue, diags := types.ListValueFrom(ctx, reservedIPObjectType, reservedIPs) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } - data.ReservedIPs = reservedIPsValue - - // If there are IPs, populate the first one's details for backwards compatibility - if len(ips) > 0 { - data.parseIP(&ips[0]) - } - } - - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) -} diff --git a/linode/networkreservedips/framework_datasource_list.go b/linode/networkreservedips/framework_datasource_list.go new file mode 100644 index 000000000..2320d3119 --- /dev/null +++ b/linode/networkreservedips/framework_datasource_list.go @@ -0,0 +1,72 @@ +package networkreservedips + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/linode/terraform-provider-linode/v2/linode/helper" +) + +func NewDataSourceList() datasource.DataSource { + return &DataSourceList{ + BaseDataSource: helper.NewBaseDataSource( + helper.BaseDataSourceConfig{ + Name: "linode_reserved_ip_list", + Schema: &frameworkDataSourceListSchema, + }, + ), + } +} + +type DataSourceList struct { + helper.BaseDataSource +} + +func (d *DataSourceList) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + tflog.Debug(ctx, "Read data.linode_reserved_ip_list") + + var data DataSourceListModel + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + client := d.Meta.Client + + // List all reserved IPs + ips, err := client.ListReservedIPAddresses(ctx, nil) + if err != nil { + resp.Diagnostics.AddError( + "Unable to list Reserved IP Addresses", + err.Error(), + ) + return + } + + reservedIPs := make([]ReservedIPObject, len(ips)) + for i, ip := range ips { + reservedIPs[i] = ReservedIPObject{ + ID: types.StringValue(ip.Address), + Address: types.StringValue(ip.Address), + Region: types.StringValue(ip.Region), + Gateway: types.StringValue(ip.Gateway), + SubnetMask: 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), + LinodeID: types.Int64Value(int64(ip.LinodeID)), + Reserved: types.BoolValue(true), // All IPs in this list are reserved + } + } + + reservedIPsValue, diags := types.ListValueFrom(ctx, reservedIPObjectType, reservedIPs) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + data.ReservedIPs = reservedIPsValue + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/linode/networkreservedips/tmpl/reserved_ip_list.gotf b/linode/networkreservedips/tmpl/reserved_ip_list.gotf new file mode 100644 index 000000000..e2357dc13 --- /dev/null +++ b/linode/networkreservedips/tmpl/reserved_ip_list.gotf @@ -0,0 +1,9 @@ +{{ define "reserved_ip_data_list" }} + +data "linode_reserved_ip_list" "test" {} + +output "reserved_ips" { + value = data.linode_reserved_ip_list.test.reserved_ips +} + +{{ end }} \ No newline at end of file diff --git a/linode/networkreservedips/tmpl/template.go b/linode/networkreservedips/tmpl/template.go index d2cc09e33..68826c4ea 100644 --- a/linode/networkreservedips/tmpl/template.go +++ b/linode/networkreservedips/tmpl/template.go @@ -6,26 +6,6 @@ import ( "github.com/linode/terraform-provider-linode/v2/linode/acceptance" ) -type TemplateData struct { - Region string - Address string -} - -func DataBasic(t *testing.T, region string) string { - return acceptance.ExecuteTemplate(t, - "reserved_ip_data_basic", TemplateData{ - Region: region, - }) -} - func DataList(t *testing.T) string { return acceptance.ExecuteTemplate(t, "reserved_ip_data_list", nil) } - -// ReserveIP generates the Terraform configuration for reserving an IP address -func ReserveIP(t *testing.T, name, region string) string { - return acceptance.ExecuteTemplate(t, - "reserved_ip_basic", TemplateData{ - Region: region, - }) -}