Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Terraform Support for Managing Reserved IP Addresses #1598

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
5 changes: 5 additions & 0 deletions linode/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ 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"
"github.com/linode/terraform-provider-linode/v2/linode/objcluster"
Expand Down Expand Up @@ -225,6 +227,7 @@ func (p *FrameworkProvider) Resources(ctx context.Context) []func() resource.Res
firewall.NewResource,
placementgroup.NewResource,
placementgroupassignment.NewResource,
networkreservedip.NewResource,
}
}

Expand Down Expand Up @@ -293,5 +296,7 @@ func (p *FrameworkProvider) DataSources(ctx context.Context) []func() datasource
placementgroups.NewDataSource,
childaccount.NewDataSource,
childaccounts.NewDataSource,
networkreservedip.NewDataSourceFetch,
networkreservedips.NewDataSourceList,
}
}
6 changes: 4 additions & 2 deletions linode/image/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
56 changes: 56 additions & 0 deletions linode/networkreservedip/datasource_fetch_model.go
Original file line number Diff line number Diff line change
@@ -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
// }
87 changes: 87 additions & 0 deletions linode/networkreservedip/datasource_fetch_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package networkreservedip

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 frameworkDataSourceFetchSchema = 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.",
Computed: 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.",
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,
},
},
}
40 changes: 40 additions & 0 deletions linode/networkreservedip/datasource_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//go:build integration || networkreservedip

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/networkreservedip/tmpl"
)

func TestAccDataSource_reservedIP(t *testing.T) {
t.Parallel()

resourceName := "data.linode_reserved_ip_fetch.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"),
),
},
},
})
}
90 changes: 90 additions & 0 deletions linode/networkreservedip/framework_datasource_fetch.go
Original file line number Diff line number Diff line change
@@ -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)...)
}
69 changes: 69 additions & 0 deletions linode/networkreservedip/framework_models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package networkreservedip

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
// }
Loading