Skip to content

Commit

Permalink
Merge pull request #732 from Juniper/688-interface-tags
Browse files Browse the repository at this point in the history
Add interface tags to ff link resource, remove some requires replace
  • Loading branch information
bwJuniper authored Jul 19, 2024
2 parents 7c3ebdd + 9abc9a8 commit 707bf53
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 185 deletions.
54 changes: 34 additions & 20 deletions apstra/blueprint/freeform_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ package blueprint
import (
"context"
"fmt"
"net"
"strings"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-nettypes/cidrtypes"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"net"
"strings"
)

type freeformEndpoint struct {
Expand All @@ -27,6 +26,7 @@ type freeformEndpoint struct {
TransformationId types.Int64 `tfsdk:"transformation_id"`
Ipv4Address cidrtypes.IPv4Prefix `tfsdk:"ipv4_address"`
Ipv6Address cidrtypes.IPv6Prefix `tfsdk:"ipv6_address"`
Tags types.Set `tfsdk:"tags"`
}

func (o freeformEndpoint) attrTypes() map[string]attr.Type {
Expand All @@ -36,68 +36,78 @@ func (o freeformEndpoint) attrTypes() map[string]attr.Type {
"transformation_id": types.Int64Type,
"ipv4_address": cidrtypes.IPv4PrefixType{},
"ipv6_address": cidrtypes.IPv6PrefixType{},
"tags": types.SetType{ElemType: types.StringType},
}
}

func (o freeformEndpoint) DatasourceAttributes() map[string]dataSourceSchema.Attribute {
return map[string]dataSourceSchema.Attribute{
"interface_name": dataSourceSchema.StringAttribute{
Computed: true,
MarkdownDescription: "the interface name",
MarkdownDescription: "The interface name, as found in the associated Device Profile, e.g. `xe-0/0/0`",
},
"interface_id": dataSourceSchema.StringAttribute{
Computed: true,
MarkdownDescription: "Graph node ID of the attached interface for this side of the link endpoint ",
MarkdownDescription: "Graph node ID of the associated interface",
},
"transformation_id": dataSourceSchema.Int64Attribute{
Computed: true,
MarkdownDescription: "ID of the transformation in the device profile",
MarkdownDescription: "ID # of the transformation in the Device Profile",
},
"ipv4_address": dataSourceSchema.StringAttribute{
Computed: true,
MarkdownDescription: "Ipv4 address of the interface",
MarkdownDescription: "Ipv4 address of the interface in CIDR notation",
CustomType: cidrtypes.IPv4PrefixType{},
},
"ipv6_address": dataSourceSchema.StringAttribute{
Computed: true,
MarkdownDescription: "Ipv6 address of the interface",
MarkdownDescription: "Ipv6 address of the interface in CIDR notation",
CustomType: cidrtypes.IPv6PrefixType{},
},
"tags": dataSourceSchema.SetAttribute{
MarkdownDescription: "Set of Tags applied to the interface",
Computed: true,
ElementType: types.StringType,
},
}
}

func (o freeformEndpoint) ResourceAttributes() map[string]resourceSchema.Attribute {
return map[string]resourceSchema.Attribute{
"interface_name": resourceSchema.StringAttribute{
Required: true,
MarkdownDescription: "the interface name",
PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
MarkdownDescription: "The interface name, as found in the associated Device Profile, e.g. `xe-0/0/0`",
Validators: []validator.String{stringvalidator.LengthAtLeast(1)},
},
"interface_id": resourceSchema.StringAttribute{
Computed: true,
MarkdownDescription: "Graph node ID of the attached interface for this side of the link endpoint.",
MarkdownDescription: "Graph node ID of the associated interface",
},
"transformation_id": resourceSchema.Int64Attribute{
Required: true,
MarkdownDescription: "ID of the transformation in the device profile",
PlanModifiers: []planmodifier.Int64{int64planmodifier.RequiresReplace()},
MarkdownDescription: "ID # of the transformation in the Device Profile",
Validators: []validator.Int64{int64validator.AtLeast(1)},
},
"ipv4_address": resourceSchema.StringAttribute{
Optional: true,
MarkdownDescription: "Ipv4 address of the interface",
MarkdownDescription: "Ipv4 address of the interface in CIDR notation",
CustomType: cidrtypes.IPv4PrefixType{},
},
"ipv6_address": resourceSchema.StringAttribute{
Optional: true,
MarkdownDescription: "Ipv6 address of the interface",
MarkdownDescription: "Ipv6 address of the interface in CIDR notation",
CustomType: cidrtypes.IPv6PrefixType{},
},
"tags": resourceSchema.SetAttribute{
MarkdownDescription: "Set of Tags applied to the interface",
Optional: true,
ElementType: types.StringType,
Validators: []validator.Set{setvalidator.SizeAtLeast(1)},
},
}
}

func (o *freeformEndpoint) request(systemId string) *apstra.FreeformEndpoint {
func (o *freeformEndpoint) request(ctx context.Context, systemId string, diags *diag.Diagnostics) *apstra.FreeformEndpoint {
var ipNet4, ipNet6 *net.IPNet
if !o.Ipv4Address.IsNull() {
var ip4 net.IP
Expand All @@ -110,6 +120,9 @@ func (o *freeformEndpoint) request(systemId string) *apstra.FreeformEndpoint {
ipNet6.IP = ip6
}

var tags []string
diags.Append(o.Tags.ElementsAs(ctx, &tags, false)...)

return &apstra.FreeformEndpoint{
SystemId: apstra.ObjectId(systemId),
Interface: apstra.FreeformInterface{
Expand All @@ -118,12 +131,13 @@ func (o *freeformEndpoint) request(systemId string) *apstra.FreeformEndpoint {
TransformationId: int(o.TransformationId.ValueInt64()),
Ipv4Address: ipNet4,
Ipv6Address: ipNet6,
Tags: tags,
},
},
}
}

func (o *freeformEndpoint) loadApiData(_ context.Context, in apstra.FreeformEndpoint, diags *diag.Diagnostics) {
func (o *freeformEndpoint) loadApiData(ctx context.Context, in apstra.FreeformEndpoint, diags *diag.Diagnostics) {
if in.Interface.Id == nil {
diags.AddError(
fmt.Sprintf("api returned nil interface Id for system %s", in.SystemId),
Expand All @@ -134,7 +148,6 @@ func (o *freeformEndpoint) loadApiData(_ context.Context, in apstra.FreeformEndp

o.InterfaceName = types.StringValue(in.Interface.Data.IfName)
o.InterfaceId = types.StringValue(in.Interface.Id.String())
//o.SystemId = types.StringValue(in.SystemId.String())
o.TransformationId = types.Int64Value(int64(in.Interface.Data.TransformationId))
o.Ipv4Address = cidrtypes.NewIPv4PrefixValue(in.Interface.Data.Ipv4Address.String())
if strings.Contains(o.Ipv4Address.ValueString(), "nil") {
Expand All @@ -144,6 +157,7 @@ func (o *freeformEndpoint) loadApiData(_ context.Context, in apstra.FreeformEndp
if strings.Contains(o.Ipv6Address.ValueString(), "nil") {
o.Ipv6Address = cidrtypes.NewIPv6PrefixNull()
}
o.Tags = utils.SetValueOrNull(ctx, types.StringType, in.Interface.Data.Tags, diags)
}

func newFreeformEndpointMap(ctx context.Context, in [2]apstra.FreeformEndpoint, diags *diag.Diagnostics) types.Map {
Expand Down
9 changes: 5 additions & 4 deletions apstra/blueprint/freeform_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ package blueprint
import (
"context"
"fmt"
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
"regexp"

"github.com/Juniper/apstra-go-sdk/apstra"
"github.com/Juniper/terraform-provider-apstra/apstra/utils"
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
dataSourceSchema "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
resourceSchema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
Expand Down Expand Up @@ -110,7 +110,8 @@ func (o FreeformLink) ResourceAttributes() map[string]resourceSchema.Attribute {
MarkdownDescription: "Freeform Link name as shown in the Web UI.",
Required: true,
Validators: []validator.String{
stringvalidator.RegexMatches(regexp.MustCompile("^[a-zA-Z0-9.-_]+$"), "name may consist only of the following characters : a-zA-Z0-9.-_")},
stringvalidator.RegexMatches(regexp.MustCompile("^[a-zA-Z0-9.-_]+$"), "name may consist only of the following characters : a-zA-Z0-9.-_"),
},
},
"speed": resourceSchema.StringAttribute{
MarkdownDescription: "Speed of the Freeform Link.",
Expand Down Expand Up @@ -159,7 +160,7 @@ func (o *FreeformLink) Request(ctx context.Context, diags *diag.Diagnostics) *ap
var epArray [2]apstra.FreeformEndpoint
var i int
for systemId, endpoint := range endpoints {
epArray[i] = *endpoint.request(systemId)
epArray[i] = *endpoint.request(ctx, systemId, diags)
i++
}

Expand Down
47 changes: 26 additions & 21 deletions apstra/resource_freeform_link_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ package tfapstra_test
import (
"context"
"fmt"
"github.com/Juniper/apstra-go-sdk/apstra"
"math/rand"
"net"
"strconv"
"testing"

"github.com/Juniper/apstra-go-sdk/apstra"
tfapstra "github.com/Juniper/terraform-provider-apstra/apstra"
testutils "github.com/Juniper/terraform-provider-apstra/apstra/test_utils"
"github.com/hashicorp/go-version"
Expand All @@ -22,20 +22,20 @@ const (
resourceFreeformLinkHcl = `
resource %q %q {
blueprint_id = %q
name = %q
tags = %s
endpoints = {
name = %q
tags = %s
endpoints = {
%q = {
interface_name = %q
transformation_id = %d
ipv4_address = %s
ipv6_address = %s
ipv4_address = %s
ipv6_address = %s
},
%q = {
interface_name = %q
transformation_id = %d
ipv4_address = %s
ipv6_address = %s
ipv4_address = %s
ipv6_address = %s
}
}
}
Expand Down Expand Up @@ -91,11 +91,13 @@ func (o resourceFreeformLink) testChecks(t testing.TB, rType, rName string) test
result.append(t, "TestCheckResourceAttr", "endpoints."+endpoint.SystemId.String()+".interface_name", endpoint.Interface.Data.IfName)
result.append(t, "TestCheckResourceAttr", "endpoints."+endpoint.SystemId.String()+".transformation_id", strconv.Itoa(endpoint.Interface.Data.TransformationId))
result.append(t, "TestCheckResourceAttrSet", "endpoints."+endpoint.SystemId.String()+".interface_id")

if endpoint.Interface.Data.Ipv4Address != nil {
result.append(t, "TestCheckResourceAttr", "endpoints."+endpoint.SystemId.String()+".ipv4_address", endpoint.Interface.Data.Ipv4Address.String())
} else {
result.append(t, "TestCheckNoResourceAttr", "endpoints."+endpoint.SystemId.String()+".ipv4_address")
}

if endpoint.Interface.Data.Ipv6Address != nil {
result.append(t, "TestCheckResourceAttr", "endpoints."+endpoint.SystemId.String()+".ipv6_address", endpoint.Interface.Data.Ipv6Address.String())
} else {
Expand All @@ -110,12 +112,14 @@ func TestResourceFreeformLink(t *testing.T) {
ctx := context.Background()
client := testutils.GetTestClient(t, ctx)
apiVersion := version.Must(version.NewVersion(client.ApiVersion()))

// create a blueprint
bp, sysIds := testutils.FfBlueprintB(t, ctx, 2)

type testStep struct {
config resourceFreeformLink
}

type testCase struct {
apiVersionConstraints version.Constraints
steps []testStep
Expand Down Expand Up @@ -166,8 +170,8 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[0],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/0",
TransformationId: 1,
IfName: "ge-0/0/1",
TransformationId: 2,
Ipv4Address: &net.IPNet{IP: net.ParseIP("192.168.10.1"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::3"), Mask: net.CIDRMask(64, 128)},
Tags: randomStrings(rand.Intn(10)+2, 6),
Expand All @@ -178,8 +182,8 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[1],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/0",
TransformationId: 1,
IfName: "ge-0/0/1",
TransformationId: 2,
Ipv4Address: &net.IPNet{IP: net.ParseIP("192.168.10.2"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::4"), Mask: net.CIDRMask(64, 128)},
Tags: randomStrings(rand.Intn(10)+2, 6),
Expand All @@ -198,7 +202,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[0],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/0",
IfName: "ge-0/0/3",
TransformationId: 1,
Ipv4Address: nil,
Ipv6Address: nil,
Expand All @@ -210,7 +214,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[1],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/0",
IfName: "ge-0/0/3",
TransformationId: 1,
Ipv4Address: nil,
Ipv6Address: nil,
Expand All @@ -220,7 +224,8 @@ func TestResourceFreeformLink(t *testing.T) {
},
},
},
}},
},
},
},
"start_maximal": {
steps: []testStep{
Expand All @@ -234,7 +239,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[0],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
IfName: "ge-0/0/4",
TransformationId: 1,
Ipv4Address: &net.IPNet{IP: net.ParseIP("10.1.1.1"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
Expand All @@ -246,7 +251,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[1],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
IfName: "ge-0/0/4",
TransformationId: 1,
Ipv4Address: &net.IPNet{IP: net.ParseIP("10.1.1.2"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::2"), Mask: net.CIDRMask(64, 128)},
Expand All @@ -267,7 +272,7 @@ func TestResourceFreeformLink(t *testing.T) {
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
TransformationId: 1,
TransformationId: 2,
Ipv4Address: nil,
Ipv6Address: nil,
Tags: nil,
Expand All @@ -279,7 +284,7 @@ func TestResourceFreeformLink(t *testing.T) {
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
TransformationId: 1,
TransformationId: 2,
Ipv4Address: nil,
Ipv6Address: nil,
Tags: nil,
Expand All @@ -299,7 +304,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[0],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
IfName: "ge-0/0/6",
TransformationId: 1,
Ipv4Address: &net.IPNet{IP: net.ParseIP("10.2.1.1"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::3"), Mask: net.CIDRMask(64, 128)},
Expand All @@ -311,7 +316,7 @@ func TestResourceFreeformLink(t *testing.T) {
SystemId: sysIds[1],
Interface: apstra.FreeformInterface{
Data: &apstra.FreeformInterfaceData{
IfName: "ge-0/0/5",
IfName: "ge-0/0/6",
TransformationId: 1,
Ipv4Address: &net.IPNet{IP: net.ParseIP("10.2.1.2"), Mask: net.CIDRMask(30, 32)},
Ipv6Address: &net.IPNet{IP: net.ParseIP("2001:db8::4"), Mask: net.CIDRMask(64, 128)},
Expand Down
Loading

0 comments on commit 707bf53

Please sign in to comment.