Skip to content

Commit

Permalink
feat(style): support pro style
Browse files Browse the repository at this point in the history
  • Loading branch information
CorentinPtrl committed Jan 23, 2025
1 parent 53c159c commit 7a9c371
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 21 deletions.
25 changes: 20 additions & 5 deletions docs/resources/node_link.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ resource "eveng_node_link" "node" {
target_node_id = eveng_node.test.id
target_port = "Gi0/1"
}
resource "eveng_start_nodes" "start" {
lab_path = eveng_lab.example.path
depends_on = [eveng_node_link.node]
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -82,5 +77,25 @@ resource "eveng_start_nodes" "start" {
### Optional

- `network_id` (Number) ID of the network.
- `style` (Attributes) Style of the link(Only for the Pro version of EVE-NG). (see [below for nested schema](#nestedatt--style))
- `target_node_id` (Number) ID of the target node.
- `target_port` (String) Target port.

<a id="nestedatt--style"></a>
### Nested Schema for `style`

Optional:

- `beziercurviness` (Number) Bezier curviness of the link.
- `color` (String) Color of the link in hexadecimal format.
- `curviness` (Number) Curviness of the link.
- `dstpos` (Number) Position of the destination.
- `label` (String) Label of the link.
- `labelpos` (Number) Position of the label.
- `linkstyle` (String) Style of the link.
- `midpoint` (Number)
- `round` (Number) Roundness of the link.
- `srcpos` (Number) Position of the source.
- `stub` (Number) Stub of the link.
- `style` (String) Style of the link.
- `width` (Number) Width of the link.
5 changes: 0 additions & 5 deletions examples/resources/eveng_node_link/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,4 @@ resource "eveng_node_link" "node" {
source_port = "Gi0/1"
target_node_id = eveng_node.test.id
target_port = "Gi0/1"
}

resource "eveng_start_nodes" "start" {
lab_path = eveng_lab.example.path
depends_on = [eveng_node_link.node]
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module terraform-provider-eveng
go 1.22.7

require (
github.com/CorentinPtrl/evengsdk v0.0.8
github.com/CorentinPtrl/evengsdk v0.0.9
github.com/hashicorp/terraform-plugin-framework v1.13.0
github.com/hashicorp/terraform-plugin-framework-validators v0.16.0
github.com/hashicorp/terraform-plugin-go v0.25.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
github.com/CorentinPtrl/evengsdk v0.0.8 h1:hOrJ/bKgYTguq8NX60rvbjpUSYw/nUIXmU0loDwfwNI=
github.com/CorentinPtrl/evengsdk v0.0.8/go.mod h1:3OEo/lyy+si+UmAOKBMnasIjFWP2RlPaHi3+13Z/KMM=
github.com/CorentinPtrl/evengsdk v0.0.9 h1:vcIYV8Qg8rxcOPhkOSPmpb6QMPd0+mj2OyUh2qAy28Q=
github.com/CorentinPtrl/evengsdk v0.0.9/go.mod h1:3OEo/lyy+si+UmAOKBMnasIjFWP2RlPaHi3+13Z/KMM=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg=
Expand Down
216 changes: 210 additions & 6 deletions internal/provider/node_link_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ package provider

import (
"context"
"encoding/json"
"fmt"
"github.com/CorentinPtrl/evengsdk"
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/float32default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int32default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
Expand All @@ -34,14 +39,31 @@ type nodeLinkResource struct {
client *evengsdk.Client
}

type StyleResourceModel struct {
Style types.String `tfsdk:"style"`
Color types.String `tfsdk:"color"`
SrcPos types.Float32 `tfsdk:"srcpos"`
DstPos types.Float32 `tfsdk:"dstpos"`
LinkStyle types.String `tfsdk:"linkstyle"`
Width types.Int32 `tfsdk:"width"`
Label types.String `tfsdk:"label"`
LabelPos types.Float32 `tfsdk:"labelpos"`
Stub types.Int32 `tfsdk:"stub"`
Curviness types.Int32 `tfsdk:"curviness"`
BezierCurviness types.Int32 `tfsdk:"beziercurviness"`
Round types.Int32 `tfsdk:"round"`
Midpoint types.Float32 `tfsdk:"midpoint"`
}

// NodeLinkResourceModel describes the resource data model.
type NodeLinkResourceModel struct {
LabPath types.String `tfsdk:"lab_path"`
NetworkId types.Int64 `tfsdk:"network_id"`
SourceNodeId types.Int64 `tfsdk:"source_node_id"`
SourcePort types.String `tfsdk:"source_port"`
TargetNodeId types.Int64 `tfsdk:"target_node_id"`
TargetPort types.String `tfsdk:"target_port"`
LabPath types.String `tfsdk:"lab_path"`
NetworkId types.Int64 `tfsdk:"network_id"`
SourceNodeId types.Int64 `tfsdk:"source_node_id"`
SourcePort types.String `tfsdk:"source_port"`
TargetNodeId types.Int64 `tfsdk:"target_node_id"`
TargetPort types.String `tfsdk:"target_port"`
Style *StyleResourceModel `tfsdk:"style"`
}

// Metadata returns the resource type name.
Expand Down Expand Up @@ -112,6 +134,95 @@ func (r *nodeLinkResource) Schema(_ context.Context, _ resource.SchemaRequest, r
Optional: true,
Description: "Target port.",
},
"style": schema.SingleNestedAttribute{
Optional: true,
Description: "Style of the link(Only for the Pro version of EVE-NG).",
Attributes: map[string]schema.Attribute{
"style": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString("Solid"),
Validators: []validator.String{
stringvalidator.OneOf("Solid", "Dashed"),
},
Description: "Style of the link.",
},
"color": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString("#3e7089"),
Description: "Color of the link in hexadecimal format.",
},
"srcpos": schema.Float32Attribute{
Optional: true,
Computed: true,
Default: float32default.StaticFloat32(0.15),
Description: "Position of the source.",
},
"dstpos": schema.Float32Attribute{
Optional: true,
Computed: true,
Default: float32default.StaticFloat32(0.85),
Description: "Position of the destination.",
},
"linkstyle": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString("Straight"),
Validators: []validator.String{
stringvalidator.OneOf("Straight", "Bezier", "Flowchart", "StateMachine"),
},
Description: "Style of the link.",
},
"width": schema.Int32Attribute{
Optional: true,
Computed: true,
Default: int32default.StaticInt32(2),
Description: "Width of the link.",
},
"label": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString(""),
Description: "Label of the link.",
},
"labelpos": schema.Float32Attribute{
Optional: true,
Computed: true,
Default: float32default.StaticFloat32(0.5),
Description: "Position of the label.",
},
"stub": schema.Int32Attribute{
Optional: true,
Computed: true,
Default: int32default.StaticInt32(0),
Description: "Stub of the link.",
},
"curviness": schema.Int32Attribute{
Optional: true,
Computed: true,
Default: int32default.StaticInt32(10),
Description: "Curviness of the link.",
},
"beziercurviness": schema.Int32Attribute{
Optional: true,
Computed: true,
Default: int32default.StaticInt32(150),
Description: "Bezier curviness of the link.",
},
"round": schema.Int32Attribute{
Optional: true,
Computed: true,
Default: int32default.StaticInt32(0),
Description: "Roundness of the link.",
},
"midpoint": schema.Float32Attribute{
Optional: true,
Computed: true,
Default: float32default.StaticFloat32(0.5),
},
},
},
},
}
}
Expand Down Expand Up @@ -151,13 +262,19 @@ func (r *nodeLinkResource) Create(ctx context.Context, req resource.CreateReques
return
}

if r.client.IsPro() {
r.MakeNodeStyle(ctx, plan)
rstyle := r.NewStyleModel(ctx, plan)
plan.Style = &rstyle
}
state := NodeLinkResourceModel{
LabPath: plan.LabPath,
NetworkId: basetypes.NewInt64Value(id),
SourceNodeId: plan.SourceNodeId,
SourcePort: plan.SourcePort,
TargetNodeId: plan.TargetNodeId,
TargetPort: plan.TargetPort,
Style: plan.Style,
}
diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
Expand Down Expand Up @@ -190,6 +307,10 @@ func (r *nodeLinkResource) Read(ctx context.Context, req resource.ReadRequest, r
return
}

if r.client.IsPro() && state.Style != nil {
style := r.NewStyleModel(ctx, state)
state.Style = &style
}
diags = resp.State.Set(ctx, state)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
Expand Down Expand Up @@ -233,6 +354,11 @@ func (r *nodeLinkResource) Update(ctx context.Context, req resource.UpdateReques
resp.Diagnostics.AddError("Failed to update node link", err.Error())
return
}
if r.client.IsPro() {
r.MakeNodeStyle(ctx, plan)
rstyle := r.NewStyleModel(ctx, plan)
plan.Style = &rstyle
}
plan.NetworkId = basetypes.NewInt64Value(id)
diags = resp.State.Set(ctx, plan)
resp.Diagnostics.Append(diags...)
Expand Down Expand Up @@ -416,3 +542,81 @@ func (r *nodeLinkResource) createOrUpdateNetwork(labPath string, networkId int,
return *network, err
}
}

func (r *nodeLinkResource) NewStyleModel(ctx context.Context, plan NodeLinkResourceModel) StyleResourceModel {
return r.GetTopologyForTargetNode(ctx, plan)
}

func (r *nodeLinkResource) GetTopologyForTargetNode(ctx context.Context, plan NodeLinkResourceModel) StyleResourceModel {
topology, err := r.client.Lab.GetTopology(plan.LabPath.ValueString())
if err != nil {
tflog.Error(ctx, fmt.Sprintf("Failed to get topology %s", err))
}
for _, node := range topology {
if node["source"].(string) == fmt.Sprintf("node%d", plan.TargetNodeId.ValueInt64()) && node["source_label"].(string) == plan.TargetPort.ValueString() {

Check failure on line 556 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)

Check failure on line 556 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)
style := node["style"].(string)

Check failure on line 557 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)

Check failure on line 557 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)
if style == "" {
style = "Solid"
}
color := node["color"].(string)

Check failure on line 561 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)

Check failure on line 561 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)
if color == "" {
color = "#3e7089"
}
srcpos, _ := strconv.ParseFloat(node["srcpos"].(string), 32)
dstpos, _ := strconv.ParseFloat(node["dstpos"].(string), 32)
linkstyle := node["linkstyle"].(string)

Check failure on line 567 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)

Check failure on line 567 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)
if linkstyle == "" {
linkstyle = "Straight"
}
width, _ := strconv.Atoi(node["width"].(string))
label := node["label"].(string)

Check failure on line 572 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)

Check failure on line 572 in internal/provider/node_link_resource.go

View workflow job for this annotation

GitHub Actions / Build

type assertion must be checked (forcetypeassert)
labelpos, _ := strconv.ParseFloat(node["labelpos"].(string), 32)
stub, _ := strconv.Atoi(node["stub"].(string))
curviness, _ := strconv.Atoi(node["curviness"].(string))
beziercurviness, _ := strconv.Atoi(node["beziercurviness"].(string))
round, _ := strconv.Atoi(node["round"].(string))
midpoint, _ := strconv.ParseFloat(node["midpoint"].(string), 32)
return StyleResourceModel{
Style: basetypes.NewStringValue(style),
Color: basetypes.NewStringValue(color),
SrcPos: basetypes.NewFloat32Value(float32(srcpos)),
DstPos: basetypes.NewFloat32Value(float32(dstpos)),
LinkStyle: basetypes.NewStringValue(linkstyle),
Width: basetypes.NewInt32Value(int32(width)),
Label: basetypes.NewStringValue(label),
LabelPos: basetypes.NewFloat32Value(float32(labelpos)),
Stub: basetypes.NewInt32Value(int32(stub)),
Curviness: basetypes.NewInt32Value(int32(curviness)),
BezierCurviness: basetypes.NewInt32Value(int32(beziercurviness)),
Round: basetypes.NewInt32Value(int32(round)),
Midpoint: basetypes.NewFloat32Value(float32(midpoint)),
}
}
}
return StyleResourceModel{}
}

func (r *nodeLinkResource) MakeNodeStyle(ctx context.Context, plan NodeLinkResourceModel) {
if plan.Style == nil {
return
}
style := evengsdk.Style{
Style: plan.Style.Style.ValueString(),
Color: plan.Style.Color.ValueString(),
Srcpos: plan.Style.SrcPos.ValueFloat32(),
Dstpos: plan.Style.DstPos.ValueFloat32(),
Linkstyle: plan.Style.LinkStyle.ValueString(),
Width: json.Number(strconv.Itoa(int(plan.Style.Width.ValueInt32()))),
Label: plan.Style.Label.ValueString(),
Labelpos: plan.Style.LabelPos.ValueFloat32(),
Stub: json.Number(strconv.Itoa(int(plan.Style.Stub.ValueInt32()))),
Curviness: json.Number(strconv.Itoa(int(plan.Style.Curviness.ValueInt32()))),
Beziercurviness: json.Number(strconv.Itoa(int(plan.Style.BezierCurviness.ValueInt32()))),
Round: json.Number(strconv.Itoa(int(plan.Style.Round.ValueInt32()))),
Midpoint: plan.Style.Midpoint.ValueFloat32(),
}
err := r.client.Node.UpdateNodeInterfaceStyleByName(plan.LabPath.ValueString(), int(plan.TargetNodeId.ValueInt64()), plan.TargetPort.ValueString(), style)
if err != nil {
tflog.Error(context.Background(), fmt.Sprintf("Failed to update node interface style %s", err))
}
}
7 changes: 7 additions & 0 deletions internal/provider/node_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

// Ensure the implementation satisfies the expected interfaces.
Expand Down Expand Up @@ -178,6 +179,12 @@ func (r *nodeResource) Create(ctx context.Context, req resource.CreateRequest, r
resp.Diagnostics.AddError("Failed to create node", err.Error())
return
}
tflog.Info(ctx, fmt.Sprintf("Created node %d", node.Id))
_, err = r.client.Node.GetNodeConfig(plan.LabPath.ValueString(), node.Id)
if err != nil {
resp.Diagnostics.AddError("Failed to get node config", err.Error())
return
}
err = r.client.Node.UpdateNodeConfig(plan.LabPath.ValueString(), node.Id, plan.Config.ValueString())
if err != nil {
resp.Diagnostics.AddError("Failed to update node config", err.Error())
Expand Down
3 changes: 1 addition & 2 deletions internal/provider/start_nodes_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ func (r *startNodesResource) Read(ctx context.Context, req resource.ReadRequest,
if resp.Diagnostics.HasError() {
return
}
state.StartTime = basetypes.NewInt64Null()
resp.State.Set(ctx, state)
resp.State.RemoveResource(ctx)
}

// Update updates the resource and sets the updated Terraform state on success.
Expand Down

0 comments on commit 7a9c371

Please sign in to comment.