Skip to content

Commit

Permalink
Refactor/resource firewall filter (#202)
Browse files Browse the repository at this point in the history
* migrate firewall_filter_rule resource to PluginFramework

* generate docs
  • Loading branch information
maksym-nazarenko committed Sep 20, 2023
1 parent 77c5535 commit e26b6a1
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 188 deletions.
20 changes: 10 additions & 10 deletions client/firewall_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import (

// FirewallFilterRule defines /ip/firewall/filter rule
type FirewallFilterRule struct {
Id string `mikrotik:".id"`
Action string `mikrotik:"action"`
Chain string `mikrotik:"chain"`
Comment string `mikrotik:"comment"`
ConnectionState types.MikrotikList `mikrotik:"connection-state"`
DestPort string `mikrotik:"dst-port"`
InInterface string `mikrotik:"in-interface"`
InInterfaceList string `mikrotik:"in-interface-list"`
OutInterfaceList string `mikrotik:"out-interface-list"`
Protocol string `mikrotik:"protocol"`
Id string `mikrotik:".id" codegen:"id,mikrotikID,terraformID"`
Action string `mikrotik:"action" codegen:"action"`
Chain string `mikrotik:"chain" codegen:"chain,required"`
Comment string `mikrotik:"comment" codegen:"comment"`
ConnectionState types.MikrotikList `mikrotik:"connection-state" codegen:"connection_state"`
DestPort string `mikrotik:"dst-port" codegen:"dst_port"`
InInterface string `mikrotik:"in-interface" codegen:"in_interface"`
InInterfaceList string `mikrotik:"in-interface-list" codegen:"in_interface_list"`
OutInterfaceList string `mikrotik:"out-interface-list" codegen:"out_interface_list"`
Protocol string `mikrotik:"protocol" codegen:"protocol"`
}

var _ Resource = (*FirewallFilterRule)(nil)
Expand Down
10 changes: 5 additions & 5 deletions docs/resources/firewall_filter_rule.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# mikrotik_firewall_filter_rule (Resource)
Manages firewall filter rules.
Creates a MikroTik FirewallFilterRule.

## Example Usage
```terraform
Expand All @@ -25,18 +25,18 @@ resource "mikrotik_firewall_filter_rule" "https" {

### Optional

- `action` (String) Action to take if packet is matched by the rule. Default: `accept`.
- `action` (String) Action to take if packet is matched by the rule.
- `comment` (String) Comment to the rule.
- `connection_state` (List of String) Interprets the connection tracking analysis data for a particular packet.
- `connection_state` (Set of String) Interprets the connection tracking analysis data for a particular packet.
- `dst_port` (String) List of destination port numbers or port number ranges.
- `in_interface` (String) Interface the packet has entered the router.
- `in_interface_list` (String) Set of interfaces defined in interface list. Works the same as in-interface.
- `out_interface_list` (String) Set of interfaces defined in interface list. Works the same as out-interface.
- `protocol` (String) Matches particular IP protocol specified by protocol name or number. Default: `tcp`.
- `protocol` (String) Matches particular IP protocol specified by protocol name or number.

### Read-Only

- `id` (String) The ID of this resource.
- `id` (String) Unique ID of this resource.

## Import
Import is supported using the following syntax:
Expand Down
3 changes: 1 addition & 2 deletions mikrotik/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ func Provider(client *mt.Mikrotik) *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"mikrotik_bgp_instance": resourceBgpInstance(),
"mikrotik_firewall_filter_rule": resourceFirewallFilterRule(),
"mikrotik_bgp_instance": resourceBgpInstance(),
},
}

Expand Down
1 change: 1 addition & 0 deletions mikrotik/provider_framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res
NewDhcpServerNetworkResource,
NewDhcpServerResource,
NewDnsRecordResource,
NewFirewallFilterRuleResource,
NewInterfaceListMemberResource,
NewInterfaceListResource,
NewInterfaceWireguardPeerResource,
Expand Down
294 changes: 123 additions & 171 deletions mikrotik/resource_firewall_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,198 +4,150 @@ import (
"context"

"github.com/ddelnano/terraform-provider-mikrotik/client"
"github.com/ddelnano/terraform-provider-mikrotik/mikrotik/internal/utils"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"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/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"

tftypes "github.com/hashicorp/terraform-plugin-framework/types"
)

func resourceFirewallFilterRule() *schema.Resource {
return &schema.Resource{
Description: "Manages firewall filter rules.",
type firewallFilterRule struct {
client *client.Mikrotik
}

CreateContext: resourceFirewallFilterRuleCreate,
ReadContext: resourceFirewallFilterRuleRead,
UpdateContext: resourceFirewallFilterRuleUpdate,
DeleteContext: resourceFirewallFilterRuleDelete,
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &firewallFilterRule{}
_ resource.ResourceWithConfigure = &firewallFilterRule{}
_ resource.ResourceWithImportState = &firewallFilterRule{}
)

Importer: &schema.ResourceImporter{
StateContext: utils.ImportStateContextUppercaseWrapper(schema.ImportStatePassthroughContext),
},
// NewFirewallFilterRuleResource is a helper function to simplify the provider implementation.
func NewFirewallFilterRuleResource() resource.Resource {
return &firewallFilterRule{}
}

Schema: firewallFilterRuleSchema(),
func (r *firewallFilterRule) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}

r.client = req.ProviderData.(*client.Mikrotik)
}

func firewallFilterRuleSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
},
"action": {
Type: schema.TypeString,
Optional: true,
Default: "accept",
Description: "Action to take if packet is matched by the rule.",
},
"chain": {
Type: schema.TypeString,
Required: true,
Description: "Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created.",
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "Comment to the rule.",
},
"connection_state": {
Type: schema.TypeList,
Optional: true,
Description: "Interprets the connection tracking analysis data for a particular packet.",
Elem: &schema.Schema{
Type: schema.TypeString,
// Metadata returns the resource type name.
func (r *firewallFilterRule) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_firewall_filter_rule"
}

// Schema defines the schema for the resource.
func (s *firewallFilterRule) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Creates a MikroTik FirewallFilterRule.",
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
Description: "Unique ID of this resource.",
},
"action": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString("accept"),
Description: "Action to take if packet is matched by the rule.",
},
"chain": schema.StringAttribute{
Required: true,
Description: "Specifies to which chain rule will be added. If the input does not match the name of an already defined chain, a new chain will be created.",
},
"comment": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Comment to the rule.",
},
"connection_state": schema.SetAttribute{
Optional: true,
Computed: true,
ElementType: tftypes.StringType,
Description: "Interprets the connection tracking analysis data for a particular packet.",
},
"dst_port": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "List of destination port numbers or port number ranges.",
},
"in_interface": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Interface the packet has entered the router.",
},
"in_interface_list": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Set of interfaces defined in interface list. Works the same as in-interface.",
},
"out_interface_list": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Set of interfaces defined in interface list. Works the same as out-interface.",
},
"protocol": schema.StringAttribute{
Optional: true,
Computed: true,
Default: stringdefault.StaticString("tcp"),
Description: "Matches particular IP protocol specified by protocol name or number.",
},
},
"dst_port": {
Type: schema.TypeString,
Optional: true,
Description: "List of destination port numbers or port number ranges.",
},
"in_interface": {
Type: schema.TypeString,
Optional: true,
Description: "Interface the packet has entered the router.",
},
"in_interface_list": {
Type: schema.TypeString,
Optional: true,
Description: "Set of interfaces defined in interface list. Works the same as in-interface.",
},
"out_interface_list": {
Type: schema.TypeString,
Optional: true,
Description: "Set of interfaces defined in interface list. Works the same as out-interface.",
},
"protocol": {
Type: schema.TypeString,
Optional: true,
Default: "tcp",
Description: "Matches particular IP protocol specified by protocol name or number.",
},
}
}

func resourceFirewallFilterRuleCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*client.Mikrotik)

created, err := c.AddFirewallFilterRule(dataToFirewallFilterRule(d))
if err != nil {
return diag.FromErr(err)
}
d.SetId(created.Id)

return resourceFirewallFilterRuleRead(ctx, d, m)
// Create creates the resource and sets the initial Terraform state.
func (r *firewallFilterRule) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var terraformModel firewallFilterRuleModel
var mikrotikModel client.FirewallFilterRule
GenericCreateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp)
}

func resourceFirewallFilterRuleRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*client.Mikrotik)
found, err := c.FindFirewallFilterRule(d.Id())
if err != nil {
return diag.FromErr(err)
}

return recordFirewallFilterRuleToData(found, d)
// Read refreshes the Terraform state with the latest data.
func (r *firewallFilterRule) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var terraformModel firewallFilterRuleModel
var mikrotikModel client.FirewallFilterRule
GenericReadResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp)
}

func resourceFirewallFilterRuleUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*client.Mikrotik)
_, err := c.UpdateFirewallFilterRule(dataToFirewallFilterRule(d))
if err != nil {
return diag.FromErr(err)
}

return nil
// Update updates the resource and sets the updated Terraform state on success.
func (r *firewallFilterRule) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var terraformModel firewallFilterRuleModel
var mikrotikModel client.FirewallFilterRule
GenericUpdateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp)
}

func resourceFirewallFilterRuleDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
c := m.(*client.Mikrotik)
if err := c.DeleteFirewallFilterRule(d.Id()); err != nil {
return diag.FromErr(err)
}

return nil
// Delete deletes the resource and removes the Terraform state on success.
func (r *firewallFilterRule) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var terraformModel firewallFilterRuleModel
var mikrotikModel client.FirewallFilterRule
GenericDeleteResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp)
}

func dataToFirewallFilterRule(d *schema.ResourceData) *client.FirewallFilterRule {
getStringList := func(data interface{}) []string {
ret := []string{}
if data == nil {
return ret
}

for _, v := range data.([]interface{}) {
if s, ok := v.(string); ok {
ret = append(ret, s)
}
}

return ret
}

return &client.FirewallFilterRule{
Id: d.Id(),
Action: d.Get("action").(string),
Chain: d.Get("chain").(string),
Comment: d.Get("comment").(string),
ConnectionState: getStringList(d.Get("connection_state")),
DestPort: d.Get("dst_port").(string),
InInterface: d.Get("in_interface").(string),
InInterfaceList: d.Get("in_interface_list").(string),
OutInterfaceList: d.Get("out_interface_list").(string),
Protocol: d.Get("protocol").(string),
}
func (r *firewallFilterRule) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
// Retrieve import ID and save to id attribute
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

func recordFirewallFilterRuleToData(r *client.FirewallFilterRule, d *schema.ResourceData) diag.Diagnostics {
var diags diag.Diagnostics

if err := d.Set("action", r.Action); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("chain", r.Chain); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("comment", r.Comment); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("connection_state", r.ConnectionState); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("dst_port", r.DestPort); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("in_interface", r.InInterface); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("in_interface_list", r.InInterfaceList); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("out_interface_list", r.OutInterfaceList); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

if err := d.Set("protocol", r.Protocol); err != nil {
diags = append(diags, diag.FromErr(err)...)
}

d.SetId(r.Id)

return diags
type firewallFilterRuleModel struct {
Id tftypes.String `tfsdk:"id"`
Action tftypes.String `tfsdk:"action"`
Chain tftypes.String `tfsdk:"chain"`
Comment tftypes.String `tfsdk:"comment"`
ConnectionState tftypes.Set `tfsdk:"connection_state"`
DestPort tftypes.String `tfsdk:"dst_port"`
InInterface tftypes.String `tfsdk:"in_interface"`
InInterfaceList tftypes.String `tfsdk:"in_interface_list"`
OutInterfaceList tftypes.String `tfsdk:"out_interface_list"`
Protocol tftypes.String `tfsdk:"protocol"`
}

0 comments on commit e26b6a1

Please sign in to comment.