Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/hashicorp/terraform-plugin-log v0.10.0
github.com/hashicorp/terraform-plugin-sdk/v2 v2.38.1
github.com/hashicorp/terraform-plugin-testing v1.14.0
github.com/oxidecomputer/oxide.go v0.7.1-0.20260127220329-e1ba7effd833
github.com/oxidecomputer/oxide.go v0.7.1-0.20260129184855-ef797073771e
github.com/stretchr/testify v1.11.1
)

Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -621,10 +621,8 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260127143929-a6e47816d4f4 h1:Dkktvmc6O++01SQBjjjKYcsIYC2AW8dVvkF63gZt5Sk=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260127143929-a6e47816d4f4/go.mod h1:ZlG5ri4a6ClA/yhDCbQN6m/F3d/m41XHx5s9WbfFLWc=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260127220329-e1ba7effd833 h1:Gx1sq1gGDQ2Mf/R0lXlW8TV6tPM8tcA0tDoXyuovYAQ=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260127220329-e1ba7effd833/go.mod h1:ZlG5ri4a6ClA/yhDCbQN6m/F3d/m41XHx5s9WbfFLWc=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260129184855-ef797073771e h1:B0o8R4UUTSK9au6GE4pns6wBK9GvOCZcdeyP67N10b0=
github.com/oxidecomputer/oxide.go v0.7.1-0.20260129184855-ef797073771e/go.mod h1:ZlG5ri4a6ClA/yhDCbQN6m/F3d/m41XHx5s9WbfFLWc=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
Expand Down
96 changes: 21 additions & 75 deletions internal/provider/resource_ip_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package provider
import (
"context"
"fmt"
"reflect"

"github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts"
"github.com/hashicorp/terraform-plugin-framework/diag"
Expand Down Expand Up @@ -272,22 +271,20 @@ func (r *ipPoolResource) Read(
for index, item := range ipPoolRanges.Items {
ipPoolRange := ipPoolResourceRangeModel{}

// TODO: For the time being we are using interfaces for nested allOf within oneOf objects in
// the OpenAPI spec. When we come up with a better approach this should be edited to reflect
// that.
switch item.Range.(type) {
case map[string]interface{}:
rs := item.Range.(map[string]interface{})
ipPoolRange.FirstAddress = types.StringValue(rs["first"].(string))
ipPoolRange.LastAddress = types.StringValue(rs["last"].(string))
// Extract first/last addresses from the IpRange variant
switch v := item.Range.Value.(type) {
case *oxide.Ipv4Range:
ipPoolRange.FirstAddress = types.StringValue(v.First)
ipPoolRange.LastAddress = types.StringValue(v.Last)
case *oxide.Ipv6Range:
ipPoolRange.FirstAddress = types.StringValue(v.First)
ipPoolRange.LastAddress = types.StringValue(v.Last)
default:
// Theoretically this should never happen. Just in case though!
resp.Diagnostics.AddError(
"Unable to read IP Pool ranges:",
fmt.Sprintf(
"internal error: %v is not map[string]interface{}. Debugging content: %+v. If you hit this bug, please contact support",
reflect.TypeOf(item.Range),
item.Range,
"internal error: unexpected IpRange variant type %T. If you hit this bug, please contact support",
item.Range.Value,
),
)
return
Expand Down Expand Up @@ -431,36 +428,10 @@ func (r *ipPoolResource) Delete(
)

for _, item := range ranges.Items {
var ipRange oxide.IpRange
rs := item.Range.(map[string]interface{})
if isIPv4(rs["first"].(string)) {
ipRange = oxide.Ipv4Range{
First: rs["first"].(string),
Last: rs["last"].(string),
}
} else if isIPv6(rs["first"].(string)) {
ipRange = oxide.Ipv6Range{
First: rs["first"].(string),
Last: rs["last"].(string),
}
} else {
// This should never happen as we are retrieving information from Nexus. If we do
// encounter
// this error we have a huge problem.
resp.Diagnostics.AddError(
"Unable to read IP Pool ranges:",
fmt.Sprintf(
"internal error: %v is not map[string]interface{}. Debugging content: %+v. If you hit this bug, please contact support",
reflect.TypeOf(item.Range),
item.Range,
),
)
return
}

// item.Range is now a struct with a Value field containing the variant
params := oxide.IpPoolRangeRemoveParams{
Pool: oxide.NameOrId(state.ID.ValueString()),
Body: &ipRange,
Body: &item.Range,
}
if err := r.client.IpPoolRangeRemove(ctx, params); err != nil {
if !is404(err) {
Expand All @@ -472,9 +443,8 @@ func (r *ipPoolResource) Delete(
}
}
tflog.Trace(ctx, fmt.Sprintf(
"removed IP pool range %v - %v from IP pool with ID: %v",
rs["first"].(string),
rs["last"].(string),
"removed IP pool range %v from IP pool with ID: %v",
item.Range.String(),
state.ID.ValueString(),
), map[string]any{"success": true})
}
Expand Down Expand Up @@ -508,26 +478,14 @@ func addRanges(
var diags diag.Diagnostics

for _, ipPoolRange := range ranges {
var body oxide.IpRange

firstAddress := ipPoolRange.FirstAddress.ValueString()
lastAddress := ipPoolRange.LastAddress.ValueString()

if isIPv4(firstAddress) {
body = oxide.Ipv4Range{
First: firstAddress,
Last: lastAddress,
}
} else if isIPv6(firstAddress) {
body = oxide.Ipv6Range{
First: firstAddress,
Last: lastAddress,
}
} else {
body, err := oxide.NewIpRange(firstAddress, lastAddress)
if err != nil {
diags.AddError(
"Error creating range within IP Pool",
fmt.Errorf("%s is neither a valid IPv4 or IPv6",
firstAddress).Error(),
err.Error(),
)
return diags
}
Expand Down Expand Up @@ -569,26 +527,14 @@ func removeRanges(
var diags diag.Diagnostics

for _, ipPoolRange := range ranges {
var body oxide.IpRange

firstAddress := ipPoolRange.FirstAddress.ValueString()
lastAddress := ipPoolRange.LastAddress.ValueString()

if isIPv4(firstAddress) {
body = oxide.Ipv4Range{
First: firstAddress,
Last: lastAddress,
}
} else if isIPv6(firstAddress) {
body = oxide.Ipv6Range{
First: firstAddress,
Last: lastAddress,
}
} else {
body, err := oxide.NewIpRange(firstAddress, lastAddress)
if err != nil {
diags.AddError(
"Error removing range within IP Pool",
fmt.Errorf("%s is neither a valid IPv4 or IPv6",
firstAddress).Error(),
err.Error(),
)
return diags
}
Expand All @@ -598,7 +544,7 @@ func removeRanges(
Body: &body,
}

err := client.IpPoolRangeRemove(ctx, params)
err = client.IpPoolRangeRemove(ctx, params)
if err != nil {
diags.AddError(
"Error removing range within IP Pool",
Expand Down
91 changes: 60 additions & 31 deletions internal/provider/resource_switch_port_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@
}

addressModel := switchPortSettingsAddressAddressModel{
Address: types.StringValue(address.Address.(string)),
Address: types.StringValue(address.Address.String()),
AddressLotID: types.StringValue(string(address.AddressLotId)),
VlanID: func() types.Int32 {
if address.VlanId == nil {
Expand Down Expand Up @@ -922,7 +922,7 @@
}
res := make([]types.String, 0)
for _, elem := range bgpPeer.AllowedExport.Value {
res = append(res, types.StringValue(elem.(string)))
res = append(res, types.StringValue(elem.String()))
}
return res
}(),
Expand All @@ -936,7 +936,7 @@
}
res := make([]types.String, 0)
for _, elem := range bgpPeer.AllowedImport.Value {
res = append(res, types.StringValue(elem.(string)))
res = append(res, types.StringValue(elem.String()))
}
return res
}(),
Expand Down Expand Up @@ -1098,7 +1098,7 @@
}

routeModel := switchPortSettingsRouteRouteModel{
Dst: types.StringValue(route.Dst.(string)),
Dst: types.StringValue(route.Dst.String()),
GW: types.StringValue(route.Gw),
RIBPriority: func() types.Int32 {
if route.RibPriority != nil {
Expand Down Expand Up @@ -1154,12 +1154,22 @@
//
// Addresses
//
var diags diag.Diagnostics
addressConfigs := make([]oxide.AddressConfig, 0)
for _, addressModel := range model.Addresses {
addresses := make([]oxide.Address, 0)
for _, addressModelNested := range addressModel.Addresses {
ipNet, err := oxide.NewIpNet(addressModelNested.Address.ValueString())
if err != nil {
diags.AddError(
"Invalid IP network address",
fmt.Sprintf("Failed to parse address %q: %s",
addressModelNested.Address.ValueString(), err.Error()),
)
return params, diags
}
address := oxide.Address{
Address: oxide.IpNet(addressModelNested.Address.ValueString()),
Address: ipNet,
AddressLot: oxide.NameOrId(addressModelNested.AddressLotID.ValueString()),
VlanId: func() *int {
if addressModelNested.VlanID.IsNull() {
Expand Down Expand Up @@ -1231,36 +1241,46 @@
}(),
}

bgpPeer.AllowedExport = oxide.ImportExportPolicy{
Type: oxide.ImportExportPolicyType(bgpModelNested.AllowedExport.Type.ValueString()),
Value: func() []oxide.IpNet {
if len(bgpModelNested.AllowedExport.Value) == 0 {
return nil
// Parse AllowedExport values
var allowedExportValues []oxide.IpNet
if len(bgpModelNested.AllowedExport.Value) > 0 {
allowedExportValues = make([]oxide.IpNet, 0, len(bgpModelNested.AllowedExport.Value))

Check failure on line 1247 in internal/provider/resource_switch_port_settings.go

View workflow job for this annotation

GitHub Actions / build-test

File is not properly formatted (golines)
for _, value := range bgpModelNested.AllowedExport.Value {
ipNet, err := oxide.NewIpNet(value.ValueString())
if err != nil {
diags.AddError(
"Invalid IP network in allowed_export",
fmt.Sprintf("Failed to parse %q: %s", value.ValueString(), err.Error()),
)
return params, diags
}
allowedExportValues = append(allowedExportValues, ipNet)
}
}
bgpPeer.AllowedExport = oxide.ImportExportPolicy{
Type: oxide.ImportExportPolicyType(bgpModelNested.AllowedExport.Type.ValueString()),
Value: allowedExportValues,
}

values := make([]oxide.IpNet, 0)
for _, value := range bgpModelNested.AllowedExport.Value {
values = append(values, oxide.IpNet(value.ValueString()))
// Parse AllowedImport values
var allowedImportValues []oxide.IpNet
if len(bgpModelNested.AllowedImport.Value) > 0 {
allowedImportValues = make([]oxide.IpNet, 0, len(bgpModelNested.AllowedImport.Value))
for _, value := range bgpModelNested.AllowedImport.Value {
ipNet, err := oxide.NewIpNet(value.ValueString())
if err != nil {
diags.AddError(
"Invalid IP network in allowed_import",
fmt.Sprintf("Failed to parse %q: %s", value.ValueString(), err.Error()),
)
return params, diags
}

return values
}(),
allowedImportValues = append(allowedImportValues, ipNet)
}
}

bgpPeer.AllowedImport = oxide.ImportExportPolicy{
Type: oxide.ImportExportPolicyType(bgpModelNested.AllowedImport.Type.ValueString()),
Value: func() []oxide.IpNet {
if len(bgpModelNested.AllowedImport.Value) == 0 {
return nil
}

values := make([]oxide.IpNet, 0)
for _, value := range bgpModelNested.AllowedImport.Value {
values = append(values, oxide.IpNet(value.ValueString()))
}

return values
}(),
Type: oxide.ImportExportPolicyType(bgpModelNested.AllowedImport.Type.ValueString()),
Value: allowedImportValues,
}

bgpPeer.Communities = func() []int {
Expand Down Expand Up @@ -1351,8 +1371,17 @@
for _, routeModel := range model.Routes {
routes := make([]oxide.Route, 0)
for _, routeModel := range routeModel.Routes {
dst, err := oxide.NewIpNet(routeModel.Dst.ValueString())
if err != nil {
diags.AddError(
"Invalid route destination",
fmt.Sprintf("Failed to parse destination %q: %s",
routeModel.Dst.ValueString(), err.Error()),
)
return params, diags
}
route := oxide.Route{
Dst: oxide.IpNet(routeModel.Dst.ValueString()),
Dst: dst,
Gw: routeModel.GW.ValueString(),
RibPriority: func() *int {
if routeModel.RIBPriority.IsNull() {
Expand Down
Loading