Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dns cache static: add regexp attribute support #231

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions client/client_crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ type (
ErrorHandler interface {
HandleError(error) error
}

// ResourceInstanceCreator interface defines methods to create new instance of a Resource.
ResourceInstanceCreator interface {
Create() Resource
}
)

// Add creates new resource on remote system
Expand Down Expand Up @@ -221,5 +226,9 @@ func (client Mikrotik) findByField(d Resource, field, value string) (Resource, e
}

func (client Mikrotik) newTargetStruct(d interface{}) reflect.Value {
if c, ok := d.(ResourceInstanceCreator); ok {
return reflect.New(reflect.Indirect(reflect.ValueOf(c.Create())).Type())
}

return reflect.New(reflect.Indirect(reflect.ValueOf(d)).Type())
}
17 changes: 7 additions & 10 deletions client/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (
type DnsRecord struct {
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"`
Regexp string `mikrotik:"regexp" codegen:"regexp"`
Ttl types.MikrotikDuration `mikrotik:"ttl" codegen:"ttl"`
Comment string `mikrotik:"comment" codegen:"comment"`
}

Expand Down Expand Up @@ -39,14 +40,6 @@ func (d *DnsRecord) AfterAddHook(r *routeros.Reply) {
d.Id = r.Done.Map["ret"]
}

func (d *DnsRecord) FindField() string {
return "name"
}

func (d *DnsRecord) FindFieldValue() string {
return d.Name
}

func (d *DnsRecord) DeleteField() string {
return "numbers"
}
Expand All @@ -65,7 +58,11 @@ func (client Mikrotik) AddDnsRecord(d *DnsRecord) (*DnsRecord, error) {
}

func (client Mikrotik) FindDnsRecord(name string) (*DnsRecord, error) {
res, err := client.Find(&DnsRecord{Name: name})
res, err := client.Find(&FindByFieldWrapper{
Resource: &DnsRecord{Name: name},
field: "name",
fieldValueFunc: func() string { return name },
})
if err != nil {
return nil, err
}
Expand Down
61 changes: 44 additions & 17 deletions client/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"reflect"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand All @@ -17,7 +18,7 @@ func TestFindDnsRecord_onNonExistantDnsRecord(t *testing.T) {
"Expecting to receive NotFound error for dns record %q", name)
}

func TestAddFindDeleteDnsRecord(t *testing.T) {
func TestDnsRecord_basic(t *testing.T) {
c := NewClient(GetConfigFromEnv())

recordName := "new_record"
Expand All @@ -34,29 +35,55 @@ func TestAddFindDeleteDnsRecord(t *testing.T) {
return
}

findRecord := &DnsRecord{}
findRecord.Name = recordName
found, err := c.Find(findRecord)
if err != nil {
t.Errorf("expected no error, got %v", err)
return
}
found, err := c.Find(&DnsRecord{Id: created.ID()})
require.NoError(t, err)

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 don't")
}
err = c.Delete(found.(Resource))
if err != nil {
t.Errorf("expected no error, got %v", err)
}

_, err = c.Find(findRecord)
created.(*DnsRecord).Comment = "updated comment"
_, err = c.Update(created)
require.NoError(t, err)
found, err = c.Find(&DnsRecord{Id: created.ID()})
require.NoError(t, err)
assert.Equal(t, created, found)

err = c.Delete(found)
assert.NoError(t, err)

_, err = c.Find(&DnsRecord{Id: created.ID()})
require.Error(t, err)

require.True(t, IsNotFoundError(err),
"expected to get NotFound error")
}

func TestDns_Regexp(t *testing.T) {
c := NewClient(GetConfigFromEnv())

recordName := "new_record"
record := &DnsRecord{
Name: recordName,
Regexp: ".*\\.domain\\.com",
Address: "10.10.10.200",
Ttl: 300,
Comment: "new record from test",
}

_, err := c.Add(record)
require.Error(t, err, "usage of 'name' and 'regexp' at the same type should result in error")

regexRecord := &DnsRecord{
Address: "10.10.10.201",
Ttl: 300,
Regexp: ".+\\.domain\\.com",
Comment: "new record from test",
}
regexCreated, err := c.Add(regexRecord)
require.NoError(t, err)
defer func() {
_ = c.Delete(regexCreated)
}()
assert.Equal(t, regexRecord, regexCreated)
}
22 changes: 8 additions & 14 deletions client/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ import (
"github.com/stretchr/testify/require"
)

var name string = "testacc"
var ranges string = "172.16.0.1-172.16.0.8,172.16.0.10"
var comment string = "terraform-acc-test-pool"
var updatedRanges string = "172.16.0.1-172.16.0.8,172.16.0.16"
var updatedComment string = "terraform acc test pool updated"

func TestAddUpdateAndDeletePool(t *testing.T) {
c := NewClient(GetConfigFromEnv())

expectedPool := &Pool{
Name: name,
Ranges: ranges,
Comment: comment,
Name: "pool-" + RandomString(),
Ranges: "172.16.0.1-172.16.0.8,172.16.0.10",
Comment: "pool comment",
}
pool, err := c.AddPool(expectedPool)

Expand All @@ -32,8 +26,8 @@ func TestAddUpdateAndDeletePool(t *testing.T) {
t.Errorf("The pool does not match what we expected. actual: %v expected: %v", pool, expectedPool)
}

expectedPool.Comment = updatedComment
expectedPool.Ranges = updatedRanges
expectedPool.Comment = "updated comment"
expectedPool.Ranges = "172.16.0.1-172.16.0.8,172.16.0.16"
pool, err = c.UpdatePool(expectedPool)

if err != nil {
Expand Down Expand Up @@ -64,9 +58,9 @@ func TestFindPoolByName_forExistingPool(t *testing.T) {
c := NewClient(GetConfigFromEnv())

p := &Pool{
Name: name,
Ranges: ranges,
Comment: comment,
Name: "pool-" + RandomString(),
Ranges: "172.16.0.1-172.16.0.8,172.16.0.10",
Comment: "existing pool",
}
pool, err := c.AddPool(p)

Expand Down
32 changes: 32 additions & 0 deletions client/resource_wrappers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package client

import "reflect"

type (
// FindByFieldWrapper changes the fields used to find the remote resource.
FindByFieldWrapper struct {
Resource
field string
fieldValueFunc func() string
}
)

var (
_ Finder = (*FindByFieldWrapper)(nil)
_ Resource = (*FindByFieldWrapper)(nil)
)

func (fw FindByFieldWrapper) FindField() string {
return fw.field
}

func (fw FindByFieldWrapper) FindFieldValue() string {
return fw.fieldValueFunc()
}

// Create satisfies ResourceInstanceCreator interface and returns new object of the wrapped resource.
func (fw FindByFieldWrapper) Create() Resource {
reflectNew := reflect.New(reflect.Indirect(reflect.ValueOf(fw.Resource)).Type())

return reflectNew.Interface().(Resource)
}
16 changes: 14 additions & 2 deletions docs/resources/dns_record.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ resource "mikrotik_dns_record" "record" {
address = "192.168.88.1"
ttl = 300
}

resource "mikrotik_dns_record" "record_regexp" {
regexp = ".+\\.example\\.domain\\.com"
address = "192.168.88.1"
ttl = 300
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -16,11 +22,12 @@ resource "mikrotik_dns_record" "record" {
### Required

- `address` (String) The A record to be returend from the DNS hostname.
- `name` (String) The name of the DNS hostname to be created.

### Optional

- `comment` (String) The comment text associated with the DNS record.
- `name` (String) The name of the DNS hostname to be created.
- `regexp` (String) Regular expression against which domain names should be verified.
- `ttl` (Number) The ttl of the DNS record.

### Read-Only
Expand All @@ -30,5 +37,10 @@ resource "mikrotik_dns_record" "record" {
## Import
Import is supported using the following syntax:
```shell
terraform import mikrotik_dns_record.record example.domain.com
# The ID argument (*2) is a MikroTik's internal id.
# It can be obtained via CLI:
#
# [admin@MikroTik] /ip dns static> :put [find where address="192.168.88.1/24"]
# *2
terraform import mikrotik_dns_record.record "*2"
```
7 changes: 6 additions & 1 deletion examples/resources/mikrotik_dns_record/import.sh
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
terraform import mikrotik_dns_record.record example.domain.com
# The ID argument (*2) is a MikroTik's internal id.
# It can be obtained via CLI:
#
# [admin@MikroTik] /ip dns static> :put [find where address="192.168.88.1/24"]
# *2
terraform import mikrotik_dns_record.record "*2"
6 changes: 6 additions & 0 deletions examples/resources/mikrotik_dns_record/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@ resource "mikrotik_dns_record" "record" {
address = "192.168.88.1"
ttl = 300
}

resource "mikrotik_dns_record" "record_regexp" {
regexp = ".+\\.example\\.domain\\.com"
address = "192.168.88.1"
ttl = 300
}
28 changes: 23 additions & 5 deletions mikrotik/resource_dns_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"github.com/ddelnano/terraform-provider-mikrotik/client"
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand All @@ -19,9 +20,10 @@ type dnsRecord struct {

// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &dnsRecord{}
_ resource.ResourceWithConfigure = &dnsRecord{}
_ resource.ResourceWithImportState = &dnsRecord{}
_ resource.Resource = &dnsRecord{}
_ resource.ResourceWithConfigure = &dnsRecord{}
_ resource.ResourceWithConfigValidators = &dnsRecord{}
_ resource.ResourceWithImportState = &dnsRecord{}
)

// NewDnsRecordResource is a helper function to simplify the provider implementation.
Expand Down Expand Up @@ -55,9 +57,15 @@ func (s *dnsRecord) Schema(_ context.Context, _ resource.SchemaRequest, resp *re
Description: "Unique ID of this resource.",
},
"name": schema.StringAttribute{
Required: true,
Optional: true,
Computed: true,
Description: "The name of the DNS hostname to be created.",
},
"regexp": schema.StringAttribute{
Optional: true,
Computed: true,
Description: "Regular expression against which domain names should be verified.",
},
"ttl": schema.Int64Attribute{
Optional: true,
Computed: true,
Expand All @@ -76,6 +84,15 @@ func (s *dnsRecord) Schema(_ context.Context, _ resource.SchemaRequest, resp *re
}
}

func (r *dnsRecord) ConfigValidators(context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{
resourcevalidator.ExactlyOneOf(
path.MatchRoot("name"),
path.MatchRoot("regexp"),
),
}
}

// 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
Expand Down Expand Up @@ -106,12 +123,13 @@ func (r *dnsRecord) Delete(ctx context.Context, req resource.DeleteRequest, resp

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)
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}

type dnsRecordModel struct {
Id tftypes.String `tfsdk:"id"`
Name tftypes.String `tfsdk:"name"`
Regexp tftypes.String `tfsdk:"regexp"`
Ttl tftypes.Int64 `tfsdk:"ttl"`
Address tftypes.String `tfsdk:"address"`
Comment tftypes.String `tfsdk:"comment"`
Expand Down
Loading
Loading