From d62c67d695237f9b7f10bc94c16149f10284b971 Mon Sep 17 00:00:00 2001 From: Maksym Date: Tue, 19 Sep 2023 20:45:47 +0300 Subject: [PATCH 1/3] migrate ip_address resource to PluginFramework (#201) --- client/ip_addr.go | 12 +- docs/resources/ip_address.md | 4 +- mikrotik/provider.go | 1 - mikrotik/provider_framework.go | 1 + mikrotik/resource_ip_address.go | 197 ++++++++++++++------------------ 5 files changed, 97 insertions(+), 118 deletions(-) diff --git a/client/ip_addr.go b/client/ip_addr.go index ad904b55..7b4b86b8 100644 --- a/client/ip_addr.go +++ b/client/ip_addr.go @@ -5,12 +5,12 @@ import ( ) type IpAddress struct { - Id string `mikrotik:".id"` - Address string `mikrotik:"address"` - Comment string `mikrotik:"comment"` - Disabled bool `mikrotik:"disabled"` - Interface string `mikrotik:"interface"` - Network string `mikrotik:"network"` + Id string `mikrotik:".id" codegen:"id,mikrotikID"` + Address string `mikrotik:"address" codegen:"address,required"` + Comment string `mikrotik:"comment" codegen:"comment"` + Disabled bool `mikrotik:"disabled" codegen:"disabled"` + Interface string `mikrotik:"interface" codegen:"interface,required"` + Network string `mikrotik:"network" codegen:"network,computed"` } var _ Resource = (*IpAddress)(nil) diff --git a/docs/resources/ip_address.md b/docs/resources/ip_address.md index 74080df5..b49320be 100644 --- a/docs/resources/ip_address.md +++ b/docs/resources/ip_address.md @@ -21,11 +21,11 @@ resource "mikrotik_ip_address" "lan" { ### Optional - `comment` (String) The comment for the IP address assignment. -- `disabled` (Boolean) Whether to disable IP address. Default: `false`. +- `disabled` (Boolean) Whether to disable IP address. ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) Unique ID of this resource. - `network` (String) IP address for the network. ## Import diff --git a/mikrotik/provider.go b/mikrotik/provider.go index 512047c5..8510e39e 100644 --- a/mikrotik/provider.go +++ b/mikrotik/provider.go @@ -71,7 +71,6 @@ func Provider(client *mt.Mikrotik) *schema.Provider { "mikrotik_dns_record": resourceRecord(), "mikrotik_interface_list_member": resourceInterfaceListMember(), "mikrotik_interface_list": resourceInterfaceList(), - "mikrotik_ip_address": resourceIpAddress(), "mikrotik_firewall_filter_rule": resourceFirewallFilterRule(), }, } diff --git a/mikrotik/provider_framework.go b/mikrotik/provider_framework.go index b3939343..751307bd 100644 --- a/mikrotik/provider_framework.go +++ b/mikrotik/provider_framework.go @@ -188,6 +188,7 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res NewDhcpServerResource, NewInterfaceWireguardPeerResource, NewInterfaceWireguardResource, + NewIpAddressResource, NewIpv6AddressResource, NewPoolResource, NewSchedulerResource, diff --git a/mikrotik/resource_ip_address.go b/mikrotik/resource_ip_address.go index 65f12e32..4bcd9917 100644 --- a/mikrotik/resource_ip_address.go +++ b/mikrotik/resource_ip_address.go @@ -4,145 +4,124 @@ 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/stringplanmodifier" + + tftypes "github.com/hashicorp/terraform-plugin-framework/types" ) -func resourceIpAddress() *schema.Resource { - return &schema.Resource{ - Description: "Assigns an IP address to an interface.", +type ipAddress struct { + client *client.Mikrotik +} - CreateContext: resourceIpAddressCreate, - ReadContext: resourceIpAddressRead, - UpdateContext: resourceIpAddressUpdate, - DeleteContext: resourceIpAddressDelete, - Importer: &schema.ResourceImporter{ - StateContext: utils.ImportStateContextUppercaseWrapper(schema.ImportStatePassthroughContext), - }, +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &ipAddress{} + _ resource.ResourceWithConfigure = &ipAddress{} + _ resource.ResourceWithImportState = &ipAddress{} +) + +// NewIpAddressResource is a helper function to simplify the provider implementation. +func NewIpAddressResource() resource.Resource { + return &ipAddress{} +} + +func (r *ipAddress) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*client.Mikrotik) +} - Schema: map[string]*schema.Schema{ - "address": { - Type: schema.TypeString, +// Metadata returns the resource type name. +func (r *ipAddress) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_ip_address" +} + +// Schema defines the schema for the resource. +func (s *ipAddress) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Assigns an IP address to an interface.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "Unique ID of this resource.", + }, + "address": schema.StringAttribute{ Required: true, Description: "The IP address and netmask of the interface using slash notation.", }, - "comment": { - Type: schema.TypeString, + "comment": schema.StringAttribute{ Optional: true, + Computed: true, Description: "The comment for the IP address assignment.", }, - "disabled": { - Type: schema.TypeBool, + "disabled": schema.BoolAttribute{ Optional: true, - Default: false, + Computed: true, Description: "Whether to disable IP address.", }, - "interface": { - Type: schema.TypeString, + "interface": schema.StringAttribute{ Required: true, Description: "The interface on which the IP address is assigned.", }, - "network": { - Type: schema.TypeString, - Computed: true, + "network": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, Description: "IP address for the network.", }, }, } } -func resourceIpAddressCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - ipAddress := prepareIpAddress(d) - - c := m.(*client.Mikrotik) - - ipaddr, err := c.AddIpAddress(ipAddress) - - if err != nil { - return diag.FromErr(err) - } - - return addrToData(ipaddr, d) +// Create creates the resource and sets the initial Terraform state. +func (r *ipAddress) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var terraformModel ipAddressModel + var mikrotikModel client.IpAddress + GenericCreateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceIpAddressRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - - ipaddr, err := c.FindIpAddress(d.Id()) - - // Clear the state if the error represents that the resource no longer exists - if client.IsNotFoundError(err) { - d.SetId("") - return nil - } - - // Make sure all other errors are propagated - if err != nil { - return diag.FromErr(err) - } - - return addrToData(ipaddr, d) +// Read refreshes the Terraform state with the latest data. +func (r *ipAddress) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var terraformModel ipAddressModel + var mikrotikModel client.IpAddress + GenericReadResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceIpAddressUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - - ipAddress := prepareIpAddress(d) - ipAddress.Id = d.Id() - - ipaddr, err := c.UpdateIpAddress(ipAddress) - - if err != nil { - return diag.FromErr(err) - } - - return addrToData(ipaddr, d) +// Update updates the resource and sets the updated Terraform state on success. +func (r *ipAddress) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var terraformModel ipAddressModel + var mikrotikModel client.IpAddress + GenericUpdateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceIpAddressDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - - err := c.DeleteIpAddress(d.Id()) - - if err != nil { - return diag.FromErr(err) - } - - d.SetId("") - return nil +// Delete deletes the resource and removes the Terraform state on success. +func (r *ipAddress) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var terraformModel ipAddressModel + var mikrotikModel client.IpAddress + GenericDeleteResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func addrToData(ipaddr *client.IpAddress, d *schema.ResourceData) diag.Diagnostics { - values := map[string]interface{}{ - "address": ipaddr.Address, - "comment": ipaddr.Comment, - "disabled": ipaddr.Disabled, - "interface": ipaddr.Interface, - "network": ipaddr.Network, - } - - d.SetId(ipaddr.Id) - - var diags diag.Diagnostics - - for key, value := range values { - if err := d.Set(key, value); err != nil { - diags = append(diags, diag.Errorf("failed to set %s: %v", key, err)...) - } - } - - return diags +func (r *ipAddress) 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 prepareIpAddress(d *schema.ResourceData) *client.IpAddress { - ipaddr := new(client.IpAddress) - - ipaddr.Comment = d.Get("comment").(string) - ipaddr.Address = d.Get("address").(string) - ipaddr.Disabled = d.Get("disabled").(bool) - ipaddr.Interface = d.Get("interface").(string) - ipaddr.Network = d.Get("network").(string) - - return ipaddr +type ipAddressModel struct { + Id tftypes.String `tfsdk:"id"` + Address tftypes.String `tfsdk:"address"` + Comment tftypes.String `tfsdk:"comment"` + Disabled tftypes.Bool `tfsdk:"disabled"` + Interface tftypes.String `tfsdk:"interface"` + Network tftypes.String `tfsdk:"network"` } From 0b1276cb60f0aec4765db85e6de7d0405ff781ad Mon Sep 17 00:00:00 2001 From: Maksym Date: Tue, 19 Sep 2023 20:46:18 +0300 Subject: [PATCH 2/3] migrate dns_record resource to PluginFramework (#198) --- client/dns.go | 10 +- docs/resources/dns_record.md | 2 +- mikrotik/provider.go | 1 - mikrotik/provider_framework.go | 1 + mikrotik/resource_dns_record.go | 198 ++++++++++++--------------- mikrotik/resource_dns_record_test.go | 9 +- 6 files changed, 96 insertions(+), 125 deletions(-) diff --git a/client/dns.go b/client/dns.go index 021c44fb..ed7a988e 100644 --- a/client/dns.go +++ b/client/dns.go @@ -6,11 +6,11 @@ import ( ) type DnsRecord struct { - Id string `mikrotik:".id"` - Name string `mikrotik:"name"` - Ttl types.MikrotikDuration `mikrotik:"ttl"` - Address string `mikrotik:"address"` - Comment string `mikrotik:"comment"` + Id string `mikrotik:".id" codegen:"id,mikrotikID"` + Name string `mikrotik:"name" codegen:"name,terraformID,required"` + Ttl types.MikrotikDuration `mikrotik:"ttl" codegen:"ttl"` + Address string `mikrotik:"address" codegen:"address,required"` + Comment string `mikrotik:"comment" codegen:"comment"` } func (d *DnsRecord) ActionToCommand(action Action) string { diff --git a/docs/resources/dns_record.md b/docs/resources/dns_record.md index 97e79a82..d71ef8fd 100644 --- a/docs/resources/dns_record.md +++ b/docs/resources/dns_record.md @@ -25,7 +25,7 @@ resource "mikrotik_dns_record" "record" { ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) Unique ID of this resource. ## Import Import is supported using the following syntax: diff --git a/mikrotik/provider.go b/mikrotik/provider.go index 8510e39e..ccae4075 100644 --- a/mikrotik/provider.go +++ b/mikrotik/provider.go @@ -68,7 +68,6 @@ func Provider(client *mt.Mikrotik) *schema.Provider { "mikrotik_bridge_port": resourceBridgePort(), "mikrotik_bridge_vlan": resourceBridgeVlan(), "mikrotik_dhcp_server_network": resourceDhcpServerNetwork(), - "mikrotik_dns_record": resourceRecord(), "mikrotik_interface_list_member": resourceInterfaceListMember(), "mikrotik_interface_list": resourceInterfaceList(), "mikrotik_firewall_filter_rule": resourceFirewallFilterRule(), diff --git a/mikrotik/provider_framework.go b/mikrotik/provider_framework.go index 751307bd..46d4202c 100644 --- a/mikrotik/provider_framework.go +++ b/mikrotik/provider_framework.go @@ -186,6 +186,7 @@ func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Res NewBridgeResource, NewDhcpLeaseResource, NewDhcpServerResource, + NewDnsRecordResource, NewInterfaceWireguardPeerResource, NewInterfaceWireguardResource, NewIpAddressResource, diff --git a/mikrotik/resource_dns_record.go b/mikrotik/resource_dns_record.go index d9b4c3ed..2cec2310 100644 --- a/mikrotik/resource_dns_record.go +++ b/mikrotik/resource_dns_record.go @@ -2,147 +2,117 @@ package mikrotik import ( "context" - "log" "github.com/ddelnano/terraform-provider-mikrotik/client" - "github.com/ddelnano/terraform-provider-mikrotik/client/types" - "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/stringplanmodifier" + + tftypes "github.com/hashicorp/terraform-plugin-framework/types" ) -func resourceRecord() *schema.Resource { - return &schema.Resource{ - Description: "Creates a DNS record on the MikroTik device.", +type dnsRecord struct { + client *client.Mikrotik +} - CreateContext: resourceServerCreate, - ReadContext: resourceServerRead, - UpdateContext: resourceServerUpdate, - DeleteContext: resourceServerDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &dnsRecord{} + _ resource.ResourceWithConfigure = &dnsRecord{} + _ resource.ResourceWithImportState = &dnsRecord{} +) + +// NewDnsRecordResource is a helper function to simplify the provider implementation. +func NewDnsRecordResource() resource.Resource { + return &dnsRecord{} +} - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, +func (r *dnsRecord) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + r.client = req.ProviderData.(*client.Mikrotik) +} + +// Metadata returns the resource type name. +func (r *dnsRecord) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_dns_record" +} + +// Schema defines the schema for the resource. +func (s *dnsRecord) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Creates a DNS record on the MikroTik device.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "Unique ID of this resource.", + }, + "name": schema.StringAttribute{ Required: true, Description: "The name of the DNS hostname to be created.", }, - "address": { - Type: schema.TypeString, + "ttl": schema.Int64Attribute{ + Optional: true, + Computed: true, + Description: "The ttl of the DNS record.", + }, + "address": schema.StringAttribute{ Required: true, Description: "The A record to be returend from the DNS hostname.", }, - "comment": { - Type: schema.TypeString, - Optional: true, - Description: "The comment text associated with the DNS record.", - }, - "ttl": { - Type: schema.TypeInt, + "comment": schema.StringAttribute{ Optional: true, Computed: true, - Description: "The ttl of the DNS record.", + Description: "The comment text associated with the DNS record.", }, }, } } -func resourceServerCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - record := prepareDnsRecord(d) - - c := m.(*client.Mikrotik) - - dnsRecord, err := c.AddDnsRecord(record) - if err != nil { - return diag.FromErr(err) - } - - return recordToData(dnsRecord, d) +// Create creates the resource and sets the initial Terraform state. +func (r *dnsRecord) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var terraformModel dnsRecordModel + var mikrotikModel client.DnsRecord + GenericCreateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceServerRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - - record, err := c.FindDnsRecord(d.Id()) - - if client.IsNotFoundError(err) { - d.SetId("") - return nil - } - if err != nil { - return diag.FromErr(err) - } - - return recordToData(record, d) +// Read refreshes the Terraform state with the latest data. +func (r *dnsRecord) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var terraformModel dnsRecordModel + var mikrotikModel client.DnsRecord + GenericReadResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceServerUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - - currentRecord, err := c.FindDnsRecord(d.Id()) - record := prepareDnsRecord(d) - record.Id = currentRecord.Id - - if err != nil { - return diag.FromErr(err) - } - - log.Printf("[DEBUG] About to update dns record with %v", record) - dnsRecord, err := c.UpdateDnsRecord(record) - if err != nil { - return diag.FromErr(err) - } - - return recordToData(dnsRecord, d) +// Update updates the resource and sets the updated Terraform state on success. +func (r *dnsRecord) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var terraformModel dnsRecordModel + var mikrotikModel client.DnsRecord + GenericUpdateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceServerDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - name := d.Id() - - c := m.(*client.Mikrotik) - - record, err := c.FindDnsRecord(name) - - if err != nil { - return diag.FromErr(err) - } - err = c.DeleteDnsRecord(record.Id) - - if err != nil { - return diag.FromErr(err) - } - d.SetId("") - return nil +// Delete deletes the resource and removes the Terraform state on success. +func (r *dnsRecord) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var terraformModel dnsRecordModel + var mikrotikModel client.DnsRecord + GenericDeleteResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func recordToData(record *client.DnsRecord, d *schema.ResourceData) diag.Diagnostics { - values := map[string]interface{}{ - "name": record.Name, - "address": record.Address, - "ttl": record.Ttl, - } - - d.SetId(record.Name) - - var diags diag.Diagnostics - - for key, value := range values { - if err := d.Set(key, value); err != nil { - diags = append(diags, diag.Errorf("failed to set %s: %v", key, err)...) - } - } - - return diags +func (r *dnsRecord) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // Retrieve import ID and save to id attribute + resource.ImportStatePassthroughID(ctx, path.Root("name"), req, resp) } -func prepareDnsRecord(d *schema.ResourceData) *client.DnsRecord { - dnsRecord := new(client.DnsRecord) - - dnsRecord.Name = d.Get("name").(string) - dnsRecord.Ttl = types.MikrotikDuration(d.Get("ttl").(int)) - dnsRecord.Address = d.Get("address").(string) - dnsRecord.Comment = d.Get("comment").(string) - - return dnsRecord +type dnsRecordModel struct { + Id tftypes.String `tfsdk:"id"` + Name tftypes.String `tfsdk:"name"` + Ttl tftypes.Int64 `tfsdk:"ttl"` + Address tftypes.String `tfsdk:"address"` + Comment tftypes.String `tfsdk:"comment"` } diff --git a/mikrotik/resource_dns_record_test.go b/mikrotik/resource_dns_record_test.go index 9c7c2982..ebac648b 100644 --- a/mikrotik/resource_dns_record_test.go +++ b/mikrotik/resource_dns_record_test.go @@ -142,8 +142,9 @@ func TestAccMikrotikDnsRecord_import(t *testing.T) { resource.TestCheckResourceAttrSet(resourceName, "id")), }, { - ResourceName: resourceName, ImportState: true, + ResourceName: resourceName, + ImportStateId: dnsName, ImportStateVerify: true, }, }, @@ -184,7 +185,7 @@ func testAccDnsRecordExists(resourceName string) resource.TestCheckFunc { c := client.NewClient(client.GetConfigFromEnv()) - dnsRecord, err := c.FindDnsRecord(rs.Primary.ID) + dnsRecord, err := c.FindDnsRecord(rs.Primary.Attributes["name"]) if err != nil { return fmt.Errorf("Unable to get the dns record with error: %v", err) @@ -194,7 +195,7 @@ func testAccDnsRecordExists(resourceName string) resource.TestCheckFunc { return fmt.Errorf("Unable to get the dns record with name: %s", dnsRecord.Name) } - if dnsRecord.Name == rs.Primary.ID { + if dnsRecord.Name == rs.Primary.Attributes["name"] { return nil } return nil @@ -208,7 +209,7 @@ func testAccCheckMikrotikDnsRecordDestroy(s *terraform.State) error { continue } - dnsRecord, err := c.FindDnsRecord(rs.Primary.ID) + dnsRecord, err := c.FindDnsRecord(rs.Primary.Attributes["name"]) if !client.IsNotFoundError(err) && err != nil { return err From 8a90b60f6f0493af4518506e6dc0be67c276477f Mon Sep 17 00:00:00 2001 From: Maksym Date: Tue, 19 Sep 2023 20:47:00 +0300 Subject: [PATCH 3/3] migrate birdge_port resource to PluginFramework (#192) --- client/bridge_port.go | 10 +- docs/resources/bridge_port.md | 6 +- mikrotik/provider.go | 1 - mikrotik/provider_framework.go | 1 + mikrotik/resource_bridge_port.go | 182 +++++++++++++------------- mikrotik/resource_bridge_port_test.go | 5 + 6 files changed, 103 insertions(+), 102 deletions(-) diff --git a/client/bridge_port.go b/client/bridge_port.go index 99fde9f2..c9105165 100644 --- a/client/bridge_port.go +++ b/client/bridge_port.go @@ -6,11 +6,11 @@ import ( // BridgePort defines port-in-bridge association type BridgePort struct { - Id string `mikrotik:".id"` - Bridge string `mikrotik:"bridge"` - Interface string `mikrotik:"interface"` - PVId int `mikrotik:"pvid"` - Comment string `mikrotik:"comment"` + Id string `mikrotik:".id" codegen:"id,mikrotikID"` + Bridge string `mikrotik:"bridge" codegen:"bridge"` + Interface string `mikrotik:"interface" codegen:"interface"` + PVId int `mikrotik:"pvid" codegen:"pvid"` + Comment string `mikrotik:"comment" codegen:"comment"` } var _ Resource = (*BridgePort)(nil) diff --git a/docs/resources/bridge_port.md b/docs/resources/bridge_port.md index a6b5df9c..b8920030 100644 --- a/docs/resources/bridge_port.md +++ b/docs/resources/bridge_port.md @@ -25,12 +25,12 @@ resource mikrotik_bridge_port "eth2port" { - `bridge` (String) The bridge interface the respective interface is grouped in. - `comment` (String) Short description for this association. -- `interface` (String) Name of the interface. Default: `*0`. -- `pvid` (Number) Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. This property only has effect when vlan-filtering is set to yes. Default: `1`. +- `interface` (String) Name of the interface. +- `pvid` (Number) Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. This property only has effect when vlan-filtering is set to yes. ### Read-Only -- `id` (String) The ID of this resource. +- `id` (String) Unique ID for the instance. ## Import Import is supported using the following syntax: diff --git a/mikrotik/provider.go b/mikrotik/provider.go index ccae4075..b8d64c22 100644 --- a/mikrotik/provider.go +++ b/mikrotik/provider.go @@ -65,7 +65,6 @@ func Provider(client *mt.Mikrotik) *schema.Provider { }, ResourcesMap: map[string]*schema.Resource{ "mikrotik_bgp_instance": resourceBgpInstance(), - "mikrotik_bridge_port": resourceBridgePort(), "mikrotik_bridge_vlan": resourceBridgeVlan(), "mikrotik_dhcp_server_network": resourceDhcpServerNetwork(), "mikrotik_interface_list_member": resourceInterfaceListMember(), diff --git a/mikrotik/provider_framework.go b/mikrotik/provider_framework.go index 46d4202c..f4bd6353 100644 --- a/mikrotik/provider_framework.go +++ b/mikrotik/provider_framework.go @@ -183,6 +183,7 @@ func (p *ProviderFramework) DataSources(ctx context.Context) []func() datasource func (p *ProviderFramework) Resources(ctx context.Context) []func() resource.Resource { return []func() resource.Resource{ NewBgpPeerResource, + NewBridgePortResource, NewBridgeResource, NewDhcpLeaseResource, NewDhcpServerResource, diff --git a/mikrotik/resource_bridge_port.go b/mikrotik/resource_bridge_port.go index 7e7a9d79..201ea85b 100644 --- a/mikrotik/resource_bridge_port.go +++ b/mikrotik/resource_bridge_port.go @@ -5,126 +5,122 @@ import ( "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-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + + tftypes "github.com/hashicorp/terraform-plugin-framework/types" ) -func resourceBridgePort() *schema.Resource { - return &schema.Resource{ - Description: "Manages ports in bridge associations.", +type bridgePort struct { + client *client.Mikrotik +} - CreateContext: resourceBridgePortCreate, - ReadContext: resourceBridgePortRead, - UpdateContext: resourceBridgePortUpdate, - DeleteContext: resourceBridgePortDelete, +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &bridgePort{} + _ resource.ResourceWithConfigure = &bridgePort{} + _ resource.ResourceWithImportState = &bridgePort{} +) - Importer: &schema.ResourceImporter{ - StateContext: utils.ImportStateContextUppercaseWrapper(schema.ImportStatePassthroughContext), - }, +// NewBridgePortResource is a helper function to simplify the provider implementation. +func NewBridgePortResource() resource.Resource { + return &bridgePort{} +} + +func (r *bridgePort) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } - Schema: map[string]*schema.Schema{ - "bridge": { - Type: schema.TypeString, + r.client = req.ProviderData.(*client.Mikrotik) +} + +// Metadata returns the resource type name. +func (r *bridgePort) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_bridge_port" +} + +// Schema defines the schema for the resource. +func (s *bridgePort) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manages ports in bridge associations.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "Unique ID for the instance.", + }, + "bridge": schema.StringAttribute{ Optional: true, + Computed: true, Description: "The bridge interface the respective interface is grouped in.", }, - "interface": { - Type: schema.TypeString, + "interface": schema.StringAttribute{ Optional: true, - Default: "*0", + Computed: true, Description: "Name of the interface.", }, - "pvid": { - Type: schema.TypeInt, - Optional: true, - Default: 1, - ValidateFunc: validation.IntBetween(1, 4094), - Description: "Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. This property only has effect when vlan-filtering is set to yes.", + "pvid": schema.Int64Attribute{ + Optional: true, + Computed: true, + Validators: []validator.Int64{ + int64validator.Between(1, 4094), + }, + Description: "Port VLAN ID (pvid) specifies which VLAN the untagged ingress traffic is assigned to. This property only has effect when vlan-filtering is set to yes.", }, - "comment": { - Type: schema.TypeString, + "comment": schema.StringAttribute{ Optional: true, + Computed: true, Description: "Short description for this association.", }, }, } } -func resourceBridgePortCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - bridgePort, err := c.AddBridgePort(dataToBridgePort(d)) - if err != nil { - return diag.FromErr(err) - } - recordBridgePortToData(bridgePort, d) - - return resourceBridgePortRead(ctx, d, m) +// Create creates the resource and sets the initial Terraform state. +func (r *bridgePort) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var terraformModel bridgePortModel + var mikrotikModel client.BridgePort + GenericCreateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceBridgePortRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Mikrotik) - bridgePort, err := c.FindBridgePort(d.Id()) - if client.IsNotFoundError(err) { - d.SetId("") - return nil - } - if err != nil { - return diag.FromErr(err) - } - - return recordBridgePortToData(bridgePort, d) +// Read refreshes the Terraform state with the latest data. +func (r *bridgePort) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var terraformModel bridgePortModel + var mikrotikModel client.BridgePort + GenericReadResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceBridgePortUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - var diags diag.Diagnostics - c := m.(*client.Mikrotik) - bridgePort := dataToBridgePort(d) - _, err := c.UpdateBridgePort(bridgePort) - if err != nil { - return diag.FromErr(err) - } - - return diags +// Update updates the resource and sets the updated Terraform state on success. +func (r *bridgePort) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var terraformModel bridgePortModel + var mikrotikModel client.BridgePort + GenericUpdateResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func resourceBridgePortDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - var diags diag.Diagnostics - c := m.(*client.Mikrotik) - err := c.DeleteBridgePort(d.Id()) - if err != nil { - return diag.FromErr(err) - } - - return diags +// Delete deletes the resource and removes the Terraform state on success. +func (r *bridgePort) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var terraformModel bridgePortModel + var mikrotikModel client.BridgePort + GenericDeleteResource(&terraformModel, &mikrotikModel, r.client)(ctx, req, resp) } -func dataToBridgePort(d *schema.ResourceData) *client.BridgePort { - return &client.BridgePort{ - Id: d.Id(), - Bridge: d.Get("bridge").(string), - Interface: d.Get("interface").(string), - PVId: d.Get("pvid").(int), - Comment: d.Get("comment").(string), - } +func (r *bridgePort) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // Retrieve import ID and save to id attribute + utils.ImportUppercaseWrapper(resource.ImportStatePassthroughID)(ctx, path.Root("id"), req, resp) } -func recordBridgePortToData(r *client.BridgePort, d *schema.ResourceData) diag.Diagnostics { - var diags diag.Diagnostics - - if err := d.Set("bridge", r.Bridge); err != nil { - diags = append(diags, diag.FromErr(err)...) - } - if err := d.Set("interface", r.Interface); err != nil { - diags = append(diags, diag.FromErr(err)...) - } - if err := d.Set("pvid", r.PVId); err != nil { - diags = append(diags, diag.FromErr(err)...) - } - if err := d.Set("comment", r.Comment); err != nil { - diags = append(diags, diag.FromErr(err)...) - } - d.SetId(r.Id) - - return diags +type bridgePortModel struct { + Id tftypes.String `tfsdk:"id"` + Bridge tftypes.String `tfsdk:"bridge"` + Interface tftypes.String `tfsdk:"interface"` + PVId tftypes.Int64 `tfsdk:"pvid"` + Comment tftypes.String `tfsdk:"comment"` } diff --git a/mikrotik/resource_bridge_port_test.go b/mikrotik/resource_bridge_port_test.go index d51b3502..1526529a 100644 --- a/mikrotik/resource_bridge_port_test.go +++ b/mikrotik/resource_bridge_port_test.go @@ -41,6 +41,11 @@ func TestBridgePort_basic(t *testing.T) { resource.TestCheckResourceAttr(rStatePath, "comment", "updated resource"), ), }, + { + ImportState: true, + ResourceName: rStatePath, + ImportStateVerify: true, + }, }, }) }