[management] Add custom dns zones (#4849)

This commit is contained in:
Bethuel Mmbaga
2026-01-16 10:12:05 +01:00
committed by GitHub
parent 291e640b28
commit 067c77e49e
36 changed files with 4837 additions and 63 deletions

View File

@@ -59,9 +59,13 @@ type Client struct {
Routes *RoutesAPI
// DNS NetBird DNS APIs
// see more: https://docs.netbird.io/api/resources/routes
// see more: https://docs.netbird.io/api/resources/dns
DNS *DNSAPI
// DNSZones NetBird DNS Zones APIs
// see more: https://docs.netbird.io/api/resources/dns-zones
DNSZones *DNSZonesAPI
// GeoLocation NetBird Geo Location APIs
// see more: https://docs.netbird.io/api/resources/geo-locations
GeoLocation *GeoLocationAPI
@@ -113,6 +117,7 @@ func (c *Client) initialize() {
c.Networks = &NetworksAPI{c}
c.Routes = &RoutesAPI{c}
c.DNS = &DNSAPI{c}
c.DNSZones = &DNSZonesAPI{c}
c.GeoLocation = &GeoLocationAPI{c}
c.Events = &EventsAPI{c}
}

View File

@@ -0,0 +1,170 @@
package rest
import (
"bytes"
"context"
"encoding/json"
"github.com/netbirdio/netbird/shared/management/http/api"
)
// DNSZonesAPI APIs for DNS Zones Management, do not use directly
type DNSZonesAPI struct {
c *Client
}
// ListZones list all DNS zones
// See more: https://docs.netbird.io/api/resources/dns-zones#list-all-dns-zones
func (a *DNSZonesAPI) ListZones(ctx context.Context) ([]api.Zone, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/dns/zones", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.Zone](resp)
return ret, err
}
// GetZone get DNS zone info
// See more: https://docs.netbird.io/api/resources/dns-zones#retrieve-a-dns-zone
func (a *DNSZonesAPI) GetZone(ctx context.Context, zoneID string) (*api.Zone, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/dns/zones/"+zoneID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.Zone](resp)
return &ret, err
}
// CreateZone create new DNS zone
// See more: https://docs.netbird.io/api/resources/dns-zones#create-a-dns-zone
func (a *DNSZonesAPI) CreateZone(ctx context.Context, request api.PostApiDnsZonesJSONRequestBody) (*api.Zone, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/dns/zones", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.Zone](resp)
return &ret, err
}
// UpdateZone update DNS zone info
// See more: https://docs.netbird.io/api/resources/dns-zones#update-a-dns-zone
func (a *DNSZonesAPI) UpdateZone(ctx context.Context, zoneID string, request api.PutApiDnsZonesZoneIdJSONRequestBody) (*api.Zone, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/dns/zones/"+zoneID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.Zone](resp)
return &ret, err
}
// DeleteZone delete DNS zone
// See more: https://docs.netbird.io/api/resources/dns-zones#delete-a-dns-zone
func (a *DNSZonesAPI) DeleteZone(ctx context.Context, zoneID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/dns/zones/"+zoneID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}
// ListRecords list all DNS records in a zone
// See more: https://docs.netbird.io/api/resources/dns-zones#list-all-dns-records
func (a *DNSZonesAPI) ListRecords(ctx context.Context, zoneID string) ([]api.DNSRecord, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/dns/zones/"+zoneID+"/records", nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[[]api.DNSRecord](resp)
return ret, err
}
// GetRecord get DNS record info
// See more: https://docs.netbird.io/api/resources/dns-zones#retrieve-a-dns-record
func (a *DNSZonesAPI) GetRecord(ctx context.Context, zoneID, recordID string) (*api.DNSRecord, error) {
resp, err := a.c.NewRequest(ctx, "GET", "/api/dns/zones/"+zoneID+"/records/"+recordID, nil, nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.DNSRecord](resp)
return &ret, err
}
// CreateRecord create new DNS record in a zone
// See more: https://docs.netbird.io/api/resources/dns-zones#create-a-dns-record
func (a *DNSZonesAPI) CreateRecord(ctx context.Context, zoneID string, request api.PostApiDnsZonesZoneIdRecordsJSONRequestBody) (*api.DNSRecord, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "POST", "/api/dns/zones/"+zoneID+"/records", bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.DNSRecord](resp)
return &ret, err
}
// UpdateRecord update DNS record info
// See more: https://docs.netbird.io/api/resources/dns-zones#update-a-dns-record
func (a *DNSZonesAPI) UpdateRecord(ctx context.Context, zoneID, recordID string, request api.PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody) (*api.DNSRecord, error) {
requestBytes, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := a.c.NewRequest(ctx, "PUT", "/api/dns/zones/"+zoneID+"/records/"+recordID, bytes.NewReader(requestBytes), nil)
if err != nil {
return nil, err
}
if resp.Body != nil {
defer resp.Body.Close()
}
ret, err := parseResponse[api.DNSRecord](resp)
return &ret, err
}
// DeleteRecord delete DNS record
// See more: https://docs.netbird.io/api/resources/dns-zones#delete-a-dns-record
func (a *DNSZonesAPI) DeleteRecord(ctx context.Context, zoneID, recordID string) error {
resp, err := a.c.NewRequest(ctx, "DELETE", "/api/dns/zones/"+zoneID+"/records/"+recordID, nil, nil)
if err != nil {
return err
}
if resp.Body != nil {
defer resp.Body.Close()
}
return nil
}

View File

@@ -0,0 +1,460 @@
//go:build integration
// +build integration
package rest_test
import (
"context"
"encoding/json"
"io"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/shared/management/client/rest"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
)
var (
testZone = api.Zone{
Id: "zone123",
Name: "test-zone",
Domain: "example.com",
Enabled: true,
EnableSearchDomain: false,
DistributionGroups: []string{"group1"},
}
testDNSRecord = api.DNSRecord{
Id: "record123",
Name: "www",
Content: "192.168.1.1",
Type: api.DNSRecordTypeA,
Ttl: 300,
}
)
func TestDNSZone_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.Zone{testZone})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.ListZones(context.Background())
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testZone, ret[0])
})
}
func TestDNSZone_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "No", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.ListZones(context.Background())
assert.Error(t, err)
assert.Equal(t, "No", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSZone_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testZone)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.GetZone(context.Background(), "zone123")
require.NoError(t, err)
assert.Equal(t, testZone, *ret)
})
}
func TestDNSZone_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.GetZone(context.Background(), "zone123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSZone_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiDnsZonesJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "test-zone", req.Name)
assert.Equal(t, "example.com", req.Domain)
retBytes, _ := json.Marshal(testZone)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
enabled := true
ret, err := c.DNSZones.CreateZone(context.Background(), api.PostApiDnsZonesJSONRequestBody{
Name: "test-zone",
Domain: "example.com",
Enabled: &enabled,
EnableSearchDomain: false,
DistributionGroups: []string{"group1"},
})
require.NoError(t, err)
assert.Equal(t, testZone, *ret)
})
}
func TestDNSZone_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Invalid request", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.CreateZone(context.Background(), api.PostApiDnsZonesJSONRequestBody{
Name: "test-zone",
Domain: "example.com",
})
assert.Error(t, err)
assert.Equal(t, "Invalid request", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSZone_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiDnsZonesZoneIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "updated-zone", req.Name)
retBytes, _ := json.Marshal(testZone)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
enabled := true
ret, err := c.DNSZones.UpdateZone(context.Background(), "zone123", api.PutApiDnsZonesZoneIdJSONRequestBody{
Name: "updated-zone",
Domain: "example.com",
Enabled: &enabled,
EnableSearchDomain: false,
DistributionGroups: []string{"group1"},
})
require.NoError(t, err)
assert.Equal(t, testZone, *ret)
})
}
func TestDNSZone_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Invalid request", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.UpdateZone(context.Background(), "zone123", api.PutApiDnsZonesZoneIdJSONRequestBody{
Name: "updated-zone",
Domain: "example.com",
})
assert.Error(t, err)
assert.Equal(t, "Invalid request", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSZone_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.DNSZones.DeleteZone(context.Background(), "zone123")
require.NoError(t, err)
})
}
func TestDNSZone_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.DNSZones.DeleteZone(context.Background(), "zone123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestDNSRecord_List_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal([]api.DNSRecord{testDNSRecord})
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.ListRecords(context.Background(), "zone123")
require.NoError(t, err)
assert.Len(t, ret, 1)
assert.Equal(t, testDNSRecord, ret[0])
})
}
func TestDNSRecord_List_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Zone not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.ListRecords(context.Background(), "zone123")
assert.Error(t, err)
assert.Equal(t, "Zone not found", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSRecord_Get_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
retBytes, _ := json.Marshal(testDNSRecord)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.GetRecord(context.Background(), "zone123", "record123")
require.NoError(t, err)
assert.Equal(t, testDNSRecord, *ret)
})
}
func TestDNSRecord_Get_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.GetRecord(context.Background(), "zone123", "record123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
assert.Empty(t, ret)
})
}
func TestDNSRecord_Create_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PostApiDnsZonesZoneIdRecordsJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "www", req.Name)
assert.Equal(t, "192.168.1.1", req.Content)
assert.Equal(t, api.DNSRecordTypeA, req.Type)
retBytes, _ := json.Marshal(testDNSRecord)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.CreateRecord(context.Background(), "zone123", api.PostApiDnsZonesZoneIdRecordsJSONRequestBody{
Name: "www",
Content: "192.168.1.1",
Type: api.DNSRecordTypeA,
Ttl: 300,
})
require.NoError(t, err)
assert.Equal(t, testDNSRecord, *ret)
})
}
func TestDNSRecord_Create_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Invalid record", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.CreateRecord(context.Background(), "zone123", api.PostApiDnsZonesZoneIdRecordsJSONRequestBody{
Name: "www",
Content: "192.168.1.1",
Type: api.DNSRecordTypeA,
Ttl: 300,
})
assert.Error(t, err)
assert.Equal(t, "Invalid record", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSRecord_Update_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
reqBytes, err := io.ReadAll(r.Body)
require.NoError(t, err)
var req api.PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody
err = json.Unmarshal(reqBytes, &req)
require.NoError(t, err)
assert.Equal(t, "api", req.Name)
assert.Equal(t, "192.168.1.2", req.Content)
retBytes, _ := json.Marshal(testDNSRecord)
_, err = w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.UpdateRecord(context.Background(), "zone123", "record123", api.PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody{
Name: "api",
Content: "192.168.1.2",
Type: api.DNSRecordTypeA,
Ttl: 300,
})
require.NoError(t, err)
assert.Equal(t, testDNSRecord, *ret)
})
}
func TestDNSRecord_Update_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Invalid record", Code: 400})
w.WriteHeader(400)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
ret, err := c.DNSZones.UpdateRecord(context.Background(), "zone123", "record123", api.PutApiDnsZonesZoneIdRecordsRecordIdJSONRequestBody{
Name: "api",
Content: "192.168.1.2",
Type: api.DNSRecordTypeA,
Ttl: 300,
})
assert.Error(t, err)
assert.Equal(t, "Invalid record", err.Error())
assert.Nil(t, ret)
})
}
func TestDNSRecord_Delete_200(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
w.WriteHeader(200)
})
err := c.DNSZones.DeleteRecord(context.Background(), "zone123", "record123")
require.NoError(t, err)
})
}
func TestDNSRecord_Delete_Err(t *testing.T) {
withMockClient(func(c *rest.Client, mux *http.ServeMux) {
mux.HandleFunc("/api/dns/zones/zone123/records/record123", func(w http.ResponseWriter, r *http.Request) {
retBytes, _ := json.Marshal(util.ErrorResponse{Message: "Not found", Code: 404})
w.WriteHeader(404)
_, err := w.Write(retBytes)
require.NoError(t, err)
})
err := c.DNSZones.DeleteRecord(context.Background(), "zone123", "record123")
assert.Error(t, err)
assert.Equal(t, "Not found", err.Error())
})
}
func TestDNSZones_Integration(t *testing.T) {
enabled := true
zoneReq := api.ZoneRequest{
Name: "test-zone",
Domain: "test.example.com",
Enabled: &enabled,
EnableSearchDomain: false,
DistributionGroups: []string{"cs1tnh0hhcjnqoiuebeg"},
}
recordReq := api.DNSRecordRequest{
Name: "api.test.example.com",
Content: "192.168.1.100",
Type: api.DNSRecordTypeA,
Ttl: 300,
}
withBlackBoxServer(t, func(c *rest.Client) {
zone, err := c.DNSZones.CreateZone(context.Background(), zoneReq)
require.NoError(t, err)
assert.Equal(t, "test-zone", zone.Name)
assert.Equal(t, "test.example.com", zone.Domain)
zones, err := c.DNSZones.ListZones(context.Background())
require.NoError(t, err)
assert.Equal(t, *zone, zones[0])
getZone, err := c.DNSZones.GetZone(context.Background(), zone.Id)
require.NoError(t, err)
assert.Equal(t, *zone, *getZone)
zoneReq.Name = "updated-zone"
updatedZone, err := c.DNSZones.UpdateZone(context.Background(), zone.Id, zoneReq)
require.NoError(t, err)
assert.Equal(t, "updated-zone", updatedZone.Name)
record, err := c.DNSZones.CreateRecord(context.Background(), zone.Id, recordReq)
require.NoError(t, err)
assert.Equal(t, "api.test.example.com", record.Name)
assert.Equal(t, "192.168.1.100", record.Content)
records, err := c.DNSZones.ListRecords(context.Background(), zone.Id)
require.NoError(t, err)
assert.Equal(t, *record, records[0])
getRecord, err := c.DNSZones.GetRecord(context.Background(), zone.Id, record.Id)
require.NoError(t, err)
assert.Equal(t, *record, *getRecord)
recordReq.Name = "www.test.example.com"
updatedRecord, err := c.DNSZones.UpdateRecord(context.Background(), zone.Id, record.Id, recordReq)
require.NoError(t, err)
assert.Equal(t, "www.test.example.com", updatedRecord.Name)
err = c.DNSZones.DeleteRecord(context.Background(), zone.Id, record.Id)
require.NoError(t, err)
records, err = c.DNSZones.ListRecords(context.Background(), zone.Id)
require.NoError(t, err)
assert.Len(t, records, 0)
err = c.DNSZones.DeleteZone(context.Background(), zone.Id)
require.NoError(t, err)
zones, err = c.DNSZones.ListZones(context.Background())
require.NoError(t, err)
assert.Len(t, zones, 0)
})
}