diff --git a/client/interface_wireguard_peer.go b/client/interface_wireguard_peer.go new file mode 100644 index 00000000..a3cba9fa --- /dev/null +++ b/client/interface_wireguard_peer.go @@ -0,0 +1,91 @@ +package client + +import ( + "github.com/go-routeros/routeros" +) + +type InterfaceWireguardPeer struct { + Id string `mikrotik:".id"` + AllowedAddress string `mikrotik:"allowed-address"` + Comment string `mikrotik:"comment"` + Disabled bool `mikrotik:"disabled"` + EndpointAddress string `mikrotik:"endpoint-address"` + EndpointPort int64 `mikrotik:"endpoint-port"` + Interface string `mikrotik:"interface"` + PersistentKeepalive int64 `mikrotik:"persistent-keepalive"` + PresharedKey string `mikrotik:"preshared-key"` + PublicKey string `mikrotik:"public-key"` +} + +func (i *InterfaceWireguardPeer) ActionToCommand(action Action) string { + return map[Action]string{ + Add: "/interface/wireguard/peers/add", + Find: "/interface/wireguard/peers/print", + List: "/interface/wireguard/peers/print", + Update: "/interface/wireguard/peers/set", + Delete: "/interface/wireguard/peers/remove", + }[action] +} + +func (i *InterfaceWireguardPeer) IDField() string { + return ".id" +} + +func (i *InterfaceWireguardPeer) ID() string { + return i.Id +} + +func (i *InterfaceWireguardPeer) SetID(id string) { + i.Id = id +} + +func (i *InterfaceWireguardPeer) AfterAddHook(r *routeros.Reply) { + i.Id = r.Done.Map["ret"] +} + +func (i *InterfaceWireguardPeer) FindField() string { + return "interface" +} + +func (i *InterfaceWireguardPeer) FindFieldValue() string { + return i.Interface +} + +func (i *InterfaceWireguardPeer) DeleteField() string { + return "numbers" +} + +func (i *InterfaceWireguardPeer) DeleteFieldValue() string { + return i.Id +} + +func (client Mikrotik) AddInterfaceWireguardPeer(i *InterfaceWireguardPeer) (*InterfaceWireguardPeer, error) { + res, err := client.Add(i) + if err != nil { + return nil, err + } + + return res.(*InterfaceWireguardPeer), nil +} + +func (client Mikrotik) FindInterfaceWireguardPeer(interfaceName string) (*InterfaceWireguardPeer, error) { + res, err := client.Find(&InterfaceWireguardPeer{Interface: interfaceName}) + if err != nil { + return nil, err + } + + return res.(*InterfaceWireguardPeer), nil +} + +func (client Mikrotik) UpdateInterfaceWireguardPeer(i *InterfaceWireguardPeer) (*InterfaceWireguardPeer, error) { + res, err := client.Update(i) + if err != nil { + return nil, err + } + + return res.(*InterfaceWireguardPeer), nil +} + +func (client Mikrotik) DeleteInterfaceWireguardPeer(id string) error { + return client.Delete(&InterfaceWireguardPeer{Id: id}) +} diff --git a/client/interface_wireguard_peer_test.go b/client/interface_wireguard_peer_test.go new file mode 100644 index 00000000..526d5a9b --- /dev/null +++ b/client/interface_wireguard_peer_test.go @@ -0,0 +1,79 @@ +package client + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFindInterfaceWireguardPeer_onNonExistantInterfacePeer(t *testing.T) { + SkipIfRouterOSV6OrEarlier(t, sysResources) + c := NewClient(GetConfigFromEnv()) + + interfaceName := "Interface peer does not exist" + _, err := c.FindInterfaceWireguardPeer(interfaceName) + + require.Truef(t, IsNotFoundError(err), + "Expecting to receive NotFound error for Interface peer `%q`, instead error was nil.", interfaceName) +} + +func TestInterfaceWireguardPeer_Crud(t *testing.T) { + SkipIfRouterOSV6OrEarlier(t, sysResources) + c := NewClient(GetConfigFromEnv()) + + name := "new_interface_wireguard" + interfaceWireguard := &InterfaceWireguard{ + Name: name, + Disabled: false, + ListenPort: 10000, + Mtu: 10001, + PrivateKey: "YOi0P0lTTiN8hAQvuRET23Srb+U7C52iOZokj0CCSkM=", + Comment: "new interface from test", + } + + createdInterface, err := c.Add(interfaceWireguard) + if err != nil { + t.Errorf("expected no error, got %v", err) + return + } + defer func() { + err = c.Delete(interfaceWireguard) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + }() + + interfaceWireguardPeer := &InterfaceWireguardPeer{ + Interface: createdInterface.(*InterfaceWireguard).Name, + Disabled: false, + Comment: "new interface from test", + } + + created, err := c.Add(interfaceWireguardPeer) + if err != nil { + t.Errorf("expected no error, got %v", err) + return + } + defer func() { + err = c.Delete(interfaceWireguardPeer) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + }() + findInterface := &InterfaceWireguardPeer{} + findInterface.Interface = createdInterface.(*InterfaceWireguard).Name + found, err := c.Find(findInterface) + if err != nil { + t.Errorf("expected no error, got %v", err) + return + } + + if _, ok := found.(Resource); !ok { + t.Error("expected found resource to implement Resource interface, but it doesn't") + return + } + if !reflect.DeepEqual(created, found) { + t.Error("expected created and found resources to be equal, but they aren't") + } +} diff --git a/docs/resources/interface_wireguard_peer.md b/docs/resources/interface_wireguard_peer.md new file mode 100644 index 00000000..63fa2567 --- /dev/null +++ b/docs/resources/interface_wireguard_peer.md @@ -0,0 +1,28 @@ +# mikrotik_interface_wireguard_peer (Resource) +Creates a Mikrotik Interface Wireguard Peer only supported by RouterOS v7+. + + + + +## Schema + +### Required + +- `interface` (String) Name of the WireGuard interface the peer belongs to. + +### Optional + +- `allowed_address` (String) List of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses. +- `comment` (String) Short description of the peer. +- `disabled` (Boolean) Boolean for whether or not the interface peer is disabled. +- `endpoint_address` (String) An endpoint IP or hostname can be left blank to allow remote connection from any address. +- `endpoint_port` (Number) An endpoint port can be left blank to allow remote connection from any port. +- `persistent_keepalive` (Number) A seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid persistently. For example, if the interface very rarely sends traffic, but it might at anytime receive traffic from a peer, and it is behind NAT, the interface might benefit from having a persistent keepalive interval of 25 seconds. +- `preshared_key` (String) A base64 preshared key. Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance. +- `public_key` (String) The remote peer's calculated public key. + +### Read-Only + +- `id` (String) Identifier of this resource assigned by RouterOS + + diff --git a/mikrotik/provider_framework.go b/mikrotik/provider_framework.go index 2af32334..6896a92d 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 NewInterfaceWireguardResource, NewSchedulerResource, NewScriptResource, + NewInterfaceWireguardPeerResource, } } diff --git a/mikrotik/resource_interface_wireguard_peer.go b/mikrotik/resource_interface_wireguard_peer.go new file mode 100644 index 00000000..d08f935c --- /dev/null +++ b/mikrotik/resource_interface_wireguard_peer.go @@ -0,0 +1,266 @@ +package mikrotik + +import ( + "context" + "fmt" + + "github.com/ddelnano/terraform-provider-mikrotik/client" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "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/booldefault" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default" + "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" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + tftypes "github.com/hashicorp/terraform-plugin-framework/types" +) + +type interfaceWireguardPeer struct { + client *client.Mikrotik +} + +// Ensure the implementation satisfies the expected interfaces. +var ( + _ resource.Resource = &interfaceWireguardPeer{} + _ resource.ResourceWithConfigure = &interfaceWireguardPeer{} + _ resource.ResourceWithImportState = &interfaceWireguardPeer{} +) + +// NewInterfaceWireguardPeerResource is a helper function to simplify the provider implementation. +func NewInterfaceWireguardPeerResource() resource.Resource { + return &interfaceWireguardPeer{} + +} + +func (i *interfaceWireguardPeer) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + i.client = req.ProviderData.(*client.Mikrotik) +} + +// Metadata returns the resource type name. +func (i *interfaceWireguardPeer) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_interface_wireguard_peer" +} + +// Schema defines the schema for the resource. +// TODO: Reevaluate the Computed schema attributes and determine if that is correct +func (i *interfaceWireguardPeer) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Creates a Mikrotik Interface Wireguard Peer only supported by RouterOS v7+.", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + Description: "Identifier of this resource assigned by RouterOS", + }, + "allowed_address": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: "List of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses.", + }, + "comment": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: "Short description of the peer.", + }, + "disabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + Default: booldefault.StaticBool(false), + Description: "Boolean for whether or not the interface peer is disabled.", + }, + "endpoint_address": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: "An endpoint IP or hostname can be left blank to allow remote connection from any address.", + }, + "endpoint_port": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(0), + Validators: []validator.Int64{ + int64validator.Between(0, 65535), + }, + Description: "An endpoint port can be left blank to allow remote connection from any port.", + }, + "interface": schema.StringAttribute{ + Required: true, + Description: "Name of the WireGuard interface the peer belongs to.", + }, + "persistent_keepalive": schema.Int64Attribute{ + Optional: true, + Computed: true, + Default: int64default.StaticInt64(0), + Validators: []validator.Int64{ + int64validator.Between(0, 65535), + }, + Description: "A seconds interval, between 1 and 65535 inclusive, of how often to send an authenticated empty packet to the peer for the purpose of keeping a stateful firewall or NAT mapping valid persistently. For example, if the interface very rarely sends traffic, but it might at anytime receive traffic from a peer, and it is behind NAT, the interface might benefit from having a persistent keepalive interval of 25 seconds.", + }, + "preshared_key": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString(""), + Description: "A base64 preshared key. Optional, and may be omitted. This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance.", + }, + "public_key": schema.StringAttribute{ + Optional: true, + Computed: true, + Description: "The remote peer's calculated public key.", + }, + }, + } +} + +// TODO: This should be migrated to the newer generic crud interface +// Create creates the resource and sets the initial Terraform state. +func (i *interfaceWireguardPeer) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan interfaceWireguardPeerModel + diags := req.Plan.Get(ctx, &plan) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + created, err := i.client.AddInterfaceWireguardPeer(modelToInterfaceWireguardPeer(&plan)) + if err != nil { + resp.Diagnostics.AddError("creation failed", err.Error()) + return + } + + resp.Diagnostics.Append(interfaceWireguardPeerToModel(created, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } +} + +// Read refreshes the Terraform state with the latest data. +func (i *interfaceWireguardPeer) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state interfaceWireguardPeerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + resource, err := i.client.FindInterfaceWireguardPeer(state.Interface.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error reading remote resource", + fmt.Sprintf("Could not read interfaceWireguardPeer with interface name %q", state.Interface.ValueString()), + ) + return + } + + resp.Diagnostics.Append(interfaceWireguardPeerToModel(resource, &state)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } +} + +// Update updates the resource and sets the updated Terraform state on success. +func (i *interfaceWireguardPeer) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan interfaceWireguardPeerModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + updated, err := i.client.UpdateInterfaceWireguardPeer(modelToInterfaceWireguardPeer(&plan)) + if err != nil { + resp.Diagnostics.AddError("update failed", err.Error()) + return + } + + resp.Diagnostics.Append(interfaceWireguardPeerToModel(updated, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } +} + +// Delete deletes the resource and removes the Terraform state on success. +func (i *interfaceWireguardPeer) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state interfaceWireguardPeerModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if err := i.client.DeleteInterfaceWireguardPeer(state.ID.ValueString()); err != nil { + resp.Diagnostics.AddError("Could not delete interfaceWireguardPeer", err.Error()) + return + } +} + +func (i *interfaceWireguardPeer) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + // Retrieve import ID and save to id attribute + resource.ImportStatePassthroughID(ctx, path.Root("interface"), req, resp) +} + +type interfaceWireguardPeerModel struct { + ID tftypes.String `tfsdk:"id"` + AllowedAddress tftypes.String `tfsdk:"allowed_address"` + Comment tftypes.String `tfsdk:"comment"` + Disabled tftypes.Bool `tfsdk:"disabled"` + EndpointAddress tftypes.String `tfsdk:"endpoint_address"` + EndpointPort tftypes.Int64 `tfsdk:"endpoint_port"` + Interface tftypes.String `tfsdk:"interface"` + PersistentKeepalive tftypes.Int64 `tfsdk:"persistent_keepalive"` + PresharedKey tftypes.String `tfsdk:"preshared_key"` + PublicKey tftypes.String `tfsdk:"public_key"` +} + +func interfaceWireguardPeerToModel(i *client.InterfaceWireguardPeer, m *interfaceWireguardPeerModel) diag.Diagnostics { + var diags diag.Diagnostics + if i == nil { + diags.AddError("Interface Wireguard Peer cannot be nil", "Cannot build model from nil object") + return diags + } + m.ID = tftypes.StringValue(i.Id) + m.AllowedAddress = tftypes.StringValue(i.AllowedAddress) + m.Comment = tftypes.StringValue(i.Comment) + m.Disabled = tftypes.BoolValue(i.Disabled) + m.EndpointAddress = tftypes.StringValue(i.EndpointAddress) + m.EndpointPort = tftypes.Int64Value(i.EndpointPort) + m.Interface = tftypes.StringValue(i.Interface) + m.PersistentKeepalive = tftypes.Int64Value(i.PersistentKeepalive) + m.PresharedKey = tftypes.StringValue(i.PresharedKey) + m.PublicKey = tftypes.StringValue(i.PublicKey) + + return diags +} + +func modelToInterfaceWireguardPeer(m *interfaceWireguardPeerModel) *client.InterfaceWireguardPeer { + return &client.InterfaceWireguardPeer{ + Id: m.ID.ValueString(), + AllowedAddress: m.AllowedAddress.ValueString(), + Comment: m.Comment.ValueString(), + Disabled: m.Disabled.ValueBool(), + EndpointAddress: m.EndpointAddress.ValueString(), + EndpointPort: m.EndpointPort.ValueInt64(), + Interface: m.Interface.ValueString(), + PersistentKeepalive: m.PersistentKeepalive.ValueInt64(), + PresharedKey: m.PresharedKey.ValueString(), + PublicKey: m.PublicKey.ValueString(), + } +} diff --git a/mikrotik/resource_interface_wireguard_peer_test.go b/mikrotik/resource_interface_wireguard_peer_test.go new file mode 100644 index 00000000..36c10ceb --- /dev/null +++ b/mikrotik/resource_interface_wireguard_peer_test.go @@ -0,0 +1,172 @@ +package mikrotik + +import ( + "fmt" + "testing" + + "github.com/ddelnano/terraform-provider-mikrotik/client" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +var origCommentPeer string = "testing" +var origAllowedAddress string = "192.168.8.1/32" +var updatedCommentPeer string = "new_comment" + +func TestAccMikrotikInterfaceWireguardPeer_create(t *testing.T) { + client.SkipIfRouterOSV6OrEarlier(t, sysResources) + + interfaceName := "tf-acc-interface-wireguard" + publicKey := "/yZWgiYAgNNSy7AIcxuEewYwOVPqJJRKG90s9ypwfiM=" + resourceName := "mikrotik_interface_wireguard_peer.bar" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + CheckDestroy: testAccCheckMikrotikInterfaceWireguardPeerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWireguardPeer(interfaceName, publicKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccInterfaceWireguardPeerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "allowed_address", origAllowedAddress), + resource.TestCheckResourceAttr(resourceName, "public_key", publicKey), + resource.TestCheckResourceAttr(resourceName, "interface", interfaceName)), + }, + }, + }) +} + +func TestAccMikrotikInterfaceWireguardPeer_updatedComment(t *testing.T) { + client.SkipIfRouterOSV6OrEarlier(t, sysResources) + + interfaceName := "tf-acc-interface-wireguard-updated" + publicKey := "/bTmUihbgNsSy2AIcxuEcwYwOVdqJJRKG51s4ypwfiM=" + resourceName := "mikrotik_interface_wireguard_peer.bar" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + CheckDestroy: testAccCheckMikrotikInterfaceWireguardPeerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWireguardPeer(interfaceName, publicKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccInterfaceWireguardPeerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "interface", interfaceName), + resource.TestCheckResourceAttr(resourceName, "public_key", publicKey), + resource.TestCheckResourceAttr(resourceName, "comment", origCommentPeer)), + }, + { + Config: testAccInterfaceWireguardPeerUpdatedComment(interfaceName, publicKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccInterfaceWireguardPeerExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "interface", interfaceName), + resource.TestCheckResourceAttr(resourceName, "comment", updatedCommentPeer)), + }, + }, + }) +} + +func TestAccMikrotikInterfaceWireguardPeer_import(t *testing.T) { + client.SkipIfRouterOSV6OrEarlier(t, sysResources) + + interfaceName := "tf-acc-interface-wireguard-import" + publicKey := "/zYaGiYbgNsSy8AIcxuEcwYwOVdqJJRKG91s9ypwfiM=" + resourceName := "mikrotik_interface_wireguard_peer.bar" + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: testAccProtoV5ProviderFactories, + CheckDestroy: testAccCheckMikrotikInterfaceWireguardPeerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccInterfaceWireguardPeer(interfaceName, publicKey), + Check: resource.ComposeAggregateTestCheckFunc( + testAccInterfaceWireguardPeerExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "interface"), + resource.TestCheckResourceAttr(resourceName, "public_key", publicKey), + resource.TestCheckResourceAttrSet(resourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: func(s *terraform.State) (string, error) { + return interfaceName, nil + }, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccInterfaceWireguardPeer(interfaceName string, publicKey string) string { + return testAccInterfaceWireguard(interfaceName) + fmt.Sprintf(` + resource "mikrotik_interface_wireguard_peer" "bar" { + interface = mikrotik_interface_wireguard.bar.name + public_key = "%s" + comment = "%s" + allowed_address = "%s" + } + `, publicKey, origCommentPeer, origAllowedAddress) +} + +func testAccInterfaceWireguardPeerUpdatedComment(interfaceName string, publicKey string) string { + return testAccInterfaceWireguard(interfaceName) + fmt.Sprintf(` + resource "mikrotik_interface_wireguard_peer" "bar" { + interface = mikrotik_interface_wireguard.bar.name + public_key = "%s" + comment = "%s" + allowed_address = "%s" + } + `, publicKey, updatedCommentPeer, origAllowedAddress) +} + +func testAccCheckMikrotikInterfaceWireguardPeerDestroy(s *terraform.State) error { + c := client.NewClient(client.GetConfigFromEnv()) + for _, rs := range s.RootModule().Resources { + if rs.Type != "mikrotik_interface_wireguard_peer" { + continue + } + + interfaceWireguardPeer, err := c.FindInterfaceWireguardPeer(rs.Primary.Attributes["interface"]) + + if !client.IsNotFoundError(err) && err != nil { + return err + } + + if interfaceWireguardPeer != nil { + return fmt.Errorf("interface wireguard peer (%s) still exists", interfaceWireguardPeer.Interface) + } + } + return nil +} + +func testAccInterfaceWireguardPeerExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("mikrotik_interface_wireguard_peer does not exist in the statefile") + } + + c := client.NewClient(client.GetConfigFromEnv()) + + interfaceWireguardPeer, err := c.FindInterfaceWireguardPeer(rs.Primary.Attributes["interface"]) + + _, ok = err.(*client.NotFound) + if !ok && err != nil { + return fmt.Errorf("Unable to get the interface wireguard peer with error: %v", err) + } + + if interfaceWireguardPeer == nil { + return fmt.Errorf("Unable to get the interface wireguard peer with interface: %s", rs.Primary.Attributes["interface"]) + } + + if interfaceWireguardPeer.Id == rs.Primary.Attributes[".id"] { + return nil + } + return nil + } +}