From 9537c72560c59cc748f136bbcbe46fbcf8521cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thor=20Anker=20Kvisg=C3=A5rd=20Lange?= Date: Mon, 4 Oct 2021 22:04:51 +0200 Subject: [PATCH] feat: Adding Bazel build setup --- .gitignore | 3 + BUILD.bazel | 6 + WORKSPACE | 36 + deps.bzl | 90 +++ go.mod | 14 + go.sum | 21 + pkg/tidydns/BUILD | 18 + pkg/tidydns/tidydns.go | 322 ++++++++ pkg/tidydns/tidydns_test.go | 1478 +++++++++++++++++++++++++++++++++++ pkg/tidydns/types.go | 28 + 10 files changed, 2016 insertions(+) create mode 100644 BUILD.bazel create mode 100644 WORKSPACE create mode 100644 deps.bzl create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/tidydns/BUILD create mode 100644 pkg/tidydns/tidydns.go create mode 100644 pkg/tidydns/tidydns_test.go create mode 100644 pkg/tidydns/types.go diff --git a/.gitignore b/.gitignore index 66fd13c..3a30386 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,8 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +# Bazel +bazel-* + # Dependency directories (remove the comment below to include it) # vendor/ diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..5aa7cd8 --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,6 @@ +load("@bazel_gazelle//:def.bzl", "gazelle") + +# gazelle:build_file_name BUILD,BUILD.bazel +# gazelle:go_naming_convention go_default_library +# gazelle:prefix github.com/neticdk/tidydns-go +gazelle(name = "gazelle") diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..1f12397 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,36 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +# +# Go and Gazelle setup +# +http_archive( + name = "io_bazel_rules_go", + sha256 = "8e968b5fcea1d2d64071872b12737bbb5514524ee5f0a4f54f5920266c261acb", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.28.0/rules_go-v0.28.0.zip", + ], +) + +http_archive( + name = "bazel_gazelle", + sha256 = "62ca106be173579c0a167deb23358fdfe71ffa1e4cfdddf5582af26520f1c66f", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.23.0/bazel-gazelle-v0.23.0.tar.gz", + ], +) + +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") + +go_rules_dependencies() + +go_register_toolchains(version = "1.17.1") + +gazelle_dependencies() + +load("//:deps.bzl", "go_dependencies") + +# gazelle:repository_macro deps.bzl%go_dependencies +go_dependencies() diff --git a/deps.bzl b/deps.bzl new file mode 100644 index 0000000..bd4013a --- /dev/null +++ b/deps.bzl @@ -0,0 +1,90 @@ +"Dependencies auto-generated by gazelle from go.mod" + +load("@bazel_gazelle//:deps.bzl", "go_repository") + +def go_dependencies(): + """Direct dependencies of Golang code inside the repositories + + Automanaged by Gazelle via: + > bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%go_dependencies --build_file_generation=on --build_file_proto_mode=disable -prune=true + """ + go_repository( + name = "com_github_creack_pty", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/creack/pty", + sum = "h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=", + version = "v1.1.9", + ) + go_repository( + name = "com_github_davecgh_go_spew", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/davecgh/go-spew", + sum = "h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=", + version = "v1.1.1", + ) + go_repository( + name = "com_github_kr_pty", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/kr/pty", + sum = "h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=", + version = "v1.1.1", + ) + go_repository( + name = "com_github_kr_text", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/kr/text", + sum = "h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=", + version = "v0.2.0", + ) + go_repository( + name = "com_github_niemeyer_pretty", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/niemeyer/pretty", + sum = "h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=", + version = "v0.0.0-20200227124842-a10e7caefd8e", + ) + go_repository( + name = "com_github_pmezard_go_difflib", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/pmezard/go-difflib", + sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", + version = "v1.0.0", + ) + go_repository( + name = "com_github_stretchr_objx", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/stretchr/objx", + sum = "h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=", + version = "v0.1.0", + ) + go_repository( + name = "com_github_stretchr_testify", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "github.com/stretchr/testify", + sum = "h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=", + version = "v1.7.0", + ) + go_repository( + name = "in_gopkg_check_v1", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "gopkg.in/check.v1", + sum = "h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=", + version = "v1.0.0-20200227125254-8fa46927fb4f", + ) + go_repository( + name = "in_gopkg_yaml_v3", + build_file_generation = "on", + build_file_proto_mode = "disable", + importpath = "gopkg.in/yaml.v3", + sum = "h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=", + version = "v3.0.0-20210107192922-496545a6307b", + ) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ad9f09c --- /dev/null +++ b/go.mod @@ -0,0 +1,14 @@ +module github.com/neticdk/tidydns-go + +go 1.16 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/stretchr/testify v1.7.0 + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) + +replace golang.org/x/net => golang.org/x/net v0.0.0-20210917221730-978cfadd31cf diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6f1958c --- /dev/null +++ b/go.sum @@ -0,0 +1,21 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/tidydns/BUILD b/pkg/tidydns/BUILD new file mode 100644 index 0000000..ec012b9 --- /dev/null +++ b/pkg/tidydns/BUILD @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = [ + "tidydns.go", + "types.go", + ], + importpath = "github.com/neticdk/tidydns-go/pkg/tidydns", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["tidydns_test.go"], + embed = [":go_default_library"], + deps = ["@com_github_stretchr_testify//assert:go_default_library"], +) diff --git a/pkg/tidydns/tidydns.go b/pkg/tidydns/tidydns.go new file mode 100644 index 0000000..0250149 --- /dev/null +++ b/pkg/tidydns/tidydns.go @@ -0,0 +1,322 @@ +package tidydns + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" +) + +type TidyDNSClient interface { + ListZones(ctx context.Context) ([]*ZoneInfo, error) + FindZoneID(ctx context.Context, name string) (int, error) + CreateRecord(ctx context.Context, zoneID int, info RecordInfo) (int, error) + UpdateRecord(ctx context.Context, zoneID int, recordID int, info RecordInfo) error + ReadRecord(ctx context.Context, zoneID int, recordID int) (*RecordInfo, error) + FindRecord(ctx context.Context, zoneID int, name string, rType RecordType) ([]*RecordInfo, error) + ListRecords(ctx context.Context, zoneID int) ([]*RecordInfo, error) + DeleteRecord(ctx context.Context, zoneID int, recordID int) error +} + +type ZoneInfo struct { + ID int + Name string +} + +type SubnetIDs struct { + SubnetID int + ZoneID int + VlanNo int +} + +type InterfaceInfo struct { + InterfaceIP string + Interfacename string +} + +type CreateInfo struct { + SubnetID int + ZoneID int + InterfaceIP string + InterfaceName string +} + +type RecordInfo struct { + ID int + Type RecordType + Name string + Description string + Destination string + TTL int + Status RecordStatus + Location LocationID +} + +type LocationID int +type RecordType int +type RecordStatus int + +const ( + RecordStatusActive RecordStatus = 0 + RecordStatusInactive RecordStatus = 1 + RecordStatusDeleted RecordStatus = 2 + + RecordTypeA RecordType = 0 + RecordTypeAPTR RecordType = 1 + RecordTypeCNAME RecordType = 2 + RecordTypeMX RecordType = 3 + RecordTypeNS RecordType = 4 + RecordTypeTXT RecordType = 5 + RecordTypeSRV RecordType = 6 + RecordTypeDS RecordType = 7 + RecordTypeSSHFP RecordType = 8 + RecordTypeTLSA RecordType = 9 + RecordTypeCAA RecordType = 10 +) + +type tidyDNSClient struct { + client *http.Client + username string + password string + baseURL string +} + +func New(baseURL, username, password string) TidyDNSClient { + return &tidyDNSClient{ + baseURL: baseURL, + username: username, + password: password, + client: &http.Client{}, + } +} + +func (c *tidyDNSClient) ListZones(ctx context.Context) ([]*ZoneInfo, error) { + var zones []zoneInfo + err := c.getData(ctx, fmt.Sprintf("%s/=/zone?type=json", c.baseURL), &zones) + if err != nil { + return nil, err + } + + result := make([]*ZoneInfo, 0) + for _, zone := range zones { + result = append(result, &ZoneInfo{ + ID: zone.ID, + Name: zone.Name, + }) + } + return result, nil +} + +func (c *tidyDNSClient) FindZoneID(ctx context.Context, name string) (int, error) { + var zones []zoneInfo + err := c.getData(ctx, fmt.Sprintf("%s/=/zone?type=json&name=%s", c.baseURL, name), &zones) + if err != nil { + return 0, err + } + + if len(zones) == 0 { + return 0, fmt.Errorf("zone not found for: %s", name) + } + + for _, z := range zones { + if z.Name == name { + return z.ID, nil + } + } + + return 0, fmt.Errorf("unable to match zone name: %s", name) +} + +func (c *tidyDNSClient) CreateRecord(ctx context.Context, zoneID int, info RecordInfo) (int, error) { + data := url.Values{ + "type": {strconv.Itoa(int(info.Type))}, + "name": {info.Name}, + "ttl": {strconv.Itoa(info.TTL)}, + "description": {info.Description}, + "status": {strconv.Itoa(int(info.Status))}, + "destination": {info.Destination}, + "location_id": {strconv.Itoa(int(info.Location))}, + } + + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/=/record/new/%d", c.baseURL, zoneID), strings.NewReader(data.Encode())) + if err != nil { + return 0, err + } + req.SetBasicAuth(c.username, c.password) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + res, err := c.client.Do(req) + if err != nil { + return 0, err + } + if res.StatusCode != http.StatusOK { + return 0, fmt.Errorf("error from tidyDNS server: %s", res.Status) + } + + req, err = http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/=/record_merged?zone_id=%d", c.baseURL, zoneID), nil) + if err != nil { + return 0, err + } + + req.SetBasicAuth(c.username, c.password) + + res, err = c.client.Do(req) + if err != nil { + return 0, err + } + if res.StatusCode != http.StatusOK { + return 0, fmt.Errorf("error from tidyDNS server: %s", res.Status) + } + + var records []recordList + err = json.NewDecoder(res.Body).Decode(&records) + if err != nil { + return 0, err + } + + for _, r := range records { + if r.Type == info.Type && r.Name == info.Name && r.Destination == info.Destination { + return r.ID, nil + } + } + + return 0, fmt.Errorf("unable to find new record") +} + +func (c *tidyDNSClient) UpdateRecord(ctx context.Context, zoneID int, recordID int, info RecordInfo) error { + data := url.Values{ + "ttl": {strconv.Itoa(info.TTL)}, + "description": {info.Description}, + "status": {strconv.Itoa(int(info.Status))}, + "destination": {info.Destination}, + "location_id": {strconv.Itoa(int(info.Location))}, + } + + req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/=/record/%d/%d", c.baseURL, recordID, zoneID), strings.NewReader(data.Encode())) + if err != nil { + return err + } + req.SetBasicAuth(c.username, c.password) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + res, err := c.client.Do(req) + if err != nil { + return err + } + if res.StatusCode != http.StatusOK { + return fmt.Errorf("error from tidyDNS server: %s", res.Status) + } + + return nil +} + +func (c *tidyDNSClient) FindRecord(ctx context.Context, zoneID int, name string, rType RecordType) ([]*RecordInfo, error) { + var records []recordList + err := c.getData(ctx, fmt.Sprintf("%s/=/record?type=json&zone=%d&name=%s", c.baseURL, zoneID, name), &records) + if err != nil { + return nil, err + } + + result := make([]*RecordInfo, 0) + for _, r := range records { + if r.Type == rType { + result = append(result, &RecordInfo{ + ID: r.ID, + Type: r.Type, + Name: r.Name, + Description: r.Description, + Destination: r.Destination, + TTL: r.TTL, + Location: r.Location, + }) + } + } + return result, nil +} + +func (c *tidyDNSClient) ListRecords(ctx context.Context, zoneID int) ([]*RecordInfo, error) { + var records []recordList + err := c.getData(ctx, fmt.Sprintf("%s/=/record_merged?type=json&zone_id=%d&showall=1", c.baseURL, zoneID), &records) + if err != nil { + return nil, err + } + + result := make([]*RecordInfo, 0) + for _, r := range records { + result = append(result, &RecordInfo{ + ID: r.ID, + Type: r.Type, + Name: r.Name, + Description: r.Description, + Destination: r.Destination, + TTL: r.TTL, + Location: r.Location, + }) + } + return result, nil +} + +func (c *tidyDNSClient) ReadRecord(ctx context.Context, zoneID int, recordID int) (*RecordInfo, error) { + var record recordRead + err := c.getData(ctx, fmt.Sprintf("%s/=/record/%d/%d", c.baseURL, zoneID, recordID), &record) + if err != nil { + return nil, err + } + + return &RecordInfo{ + ID: record.ID, + Type: record.Type, + Name: record.Name, + Description: record.Description, + Destination: record.Destination, + TTL: record.TTL, + Status: record.Status, + Location: record.Location, + }, nil +} + +func (c *tidyDNSClient) DeleteRecord(ctx context.Context, zoneID int, recordID int) error { + req, err := http.NewRequestWithContext(ctx, "DELETE", fmt.Sprintf("%s/=/record/%d/%d", c.baseURL, recordID, zoneID), nil) + if err != nil { + return err + } + + req.SetBasicAuth(c.username, c.password) + + res, err := c.client.Do(req) + if err != nil { + return err + } + if res.StatusCode != http.StatusOK { + return fmt.Errorf("error from tidyDNS server: %s", res.Status) + } + + return nil +} + +func (c *tidyDNSClient) getData(ctx context.Context, url string, value interface{}) error { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return err + } + + req.SetBasicAuth(c.username, c.password) + + res, err := c.client.Do(req) + if err != nil { + return err + } + if res.StatusCode != http.StatusOK { + return fmt.Errorf("error from tidyDNS server: %s", res.Status) + } + + err = json.NewDecoder(res.Body).Decode(value) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/tidydns/tidydns_test.go b/pkg/tidydns/tidydns_test.go new file mode 100644 index 0000000..d8595d0 --- /dev/null +++ b/pkg/tidydns/tidydns_test.go @@ -0,0 +1,1478 @@ +package tidydns + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFindZoneID(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Contains(t, req.URL.Query().Get("name"), "hackerdays.trifork.dev") + assert.Equal(t, "GET", req.Method) + rw.Write([]byte(zoneSearchResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + id, err := c.FindZoneID(context.Background(), "hackerdays.trifork.dev") + assert.NoError(t, err) + assert.Equal(t, 2926, id) +} + +func TestCreateRecord(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Write([]byte(readRecordListResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + createInfo := RecordInfo{ + Type: RecordTypeA, + Name: "tal-test", + Destination: "10.68.1.2", + Location: LocationID(0), + } + id, err := c.CreateRecord(context.Background(), 2861, createInfo) + assert.NoError(t, err) + assert.Equal(t, 64694, id) +} + +func TestReadRecord(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Contains(t, req.URL.Path, "2861") + assert.Contains(t, req.URL.Path, "64694") + assert.Equal(t, "GET", req.Method) + rw.Write([]byte(readRecordResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + info, err := c.ReadRecord(context.Background(), 2861, 64694) + + assert.NoError(t, err) + + assert.Equal(t, "10.68.1.2", info.Destination) + assert.Equal(t, "tal-test", info.Name) + assert.Equal(t, RecordTypeA, info.Type) +} + +func TestDeleteRecord(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Contains(t, req.URL.Path, "2861") + assert.Contains(t, req.URL.Path, "64694") + assert.Equal(t, "DELETE", req.Method) + rw.Write([]byte(createResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + err := c.DeleteRecord(context.Background(), 2861, 64694) + assert.NoError(t, err) +} + +func TestUpdateRecord(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, "POST", req.Method) + rw.Write([]byte(readRecordListResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + createInfo := RecordInfo{ + Destination: "10.68.1.2", + Location: LocationID(0), + } + err := c.UpdateRecord(context.Background(), 2861, 64694, createInfo) + assert.NoError(t, err) +} + +func TestFindRecord(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, "prod1-api.trifork.shared", req.URL.Query().Get("name")) + assert.Equal(t, "2861", req.URL.Query().Get("zone")) + assert.Equal(t, "GET", req.Method) + rw.Write([]byte(findRecordResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + info, err := c.FindRecord(context.Background(), 2861, "prod1-api.trifork.shared", RecordTypeA) + assert.NoError(t, err) + assert.Equal(t, "prod1-api.trifork.shared", info[0].Name) + assert.Equal(t, 65377, info[0].ID) +} + +func TestListZones(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, "GET", req.Method) + rw.Write([]byte(listZonesResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + zones, err := c.ListZones(context.Background()) + assert.NoError(t, err) + assert.Equal(t, len(zones), 4) +} + +func TestListRecords(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + assert.Equal(t, "2861", req.URL.Query().Get("zone_id")) + assert.Equal(t, "GET", req.Method) + rw.Write([]byte(listRecordsResponse)) + })) + defer server.Close() + + c := New(server.URL, "username", "password") + records, err := c.ListRecords(context.Background(), 2861) + assert.NoError(t, err) + assert.Equal(t, len(records), 22) +} + +const zoneSearchResponse = `[ + { + "soa_record": null, + "namehelper": null, + "network": null, + "customer_id": 0, + "authoritative_log": "NXDOMAIN: ASKED(ns-cloud-e4.googledomains.com ns-cloud-e2.googledomains.com ns-cloud-e3.googledomains.com ns-cloud-e1.googledomains.com)", + "class": null, + "soa_slave_retry": null, + "last_check_date": "2021-09-07 14:25:24", + "soa_ttl": null, + "autofill_template": null, + "soa_slave_refresh": null, + "serial": 8, + "type_text": "regular", + "dnssec_parent_state": -1, + "dnssec_genkeys": 1, + "update": 0, + "server_state": 0, + "masters": null, + "provision_date": "2021-09-17 08:43:21", + "soa_max_caching": null, + "range_stop": null, + "provision_log": null, + "id": 2926, + "created_date": "2021-09-07 14:25:22", + "type_has_records": 1, + "description": "Subdomain delegation for automated DNS @Hackerdays 2021", + "allow_transfer": null, + "modified_by": "tal", + "type": 0, + "alias_type": null, + "dnssec_lastsign": null, + "autofill_enable": null, + "dnssec_enable": 0, + "alias_id": null, + "provision_state": 1, + "authoritative_state": 3, + "parent_id": null, + "do_validate": 0, + "forwarders": null, + "dnssec_parent_log": null, + "status": 0, + "dnssec_monitor_enable": 0, + "alias_name": null, + "soa_contact": null, + "soa_slave_expiration": null, + "modified_date": "2021-09-17 08:43:19", + "is_private": null, + "inject_ns_enable": 1, + "zone_name": "hackerdays.trifork.dev", + "range_start": null, + "name": "hackerdays.trifork.dev" + } +]` + +const createResponse = `{ + "status": "0", + "id": 30641, + "subnet_id": 1185 +}` + +const readRecordResponse = `{ + "zone_record": 0, + "macro": 0, + "nice_macro_name": null, + "loc_name": "internal", + "modified_date": "2021-08-18 12:53:50", + "destination": "10.68.1.2", + "ttl": 0, + "status_text": "active", + "zone_status": 0, + "modified_by": "tal", + "created_by": "tal", + "status": 0, + "type_name": "A", + "namehelper": null, + "macro_name": null, + "tidy_record": 1, + "zone_type": 0, + "external_table": "tidy_record", + "description": "Test A record creation", + "location_id": 1, + "port": null, + "name": "tal-test", + "created_date": "2021-08-18 12:53:50", + "history": 0, + "type": 0, + "value": 0, + "zone_id": 2861, + "zone_name": "k8s.netic.dk", + "customer_id": 0, + "extra_ip": null, + "active": 1, + "coalesce_name": "tal-test", + "weight": null, + "id": 64694, + "coalesce_macro_dest": "10.68.1.2", + "record_fullname": "tal-test.k8s.netic.dk" +}` + +const readRecordListResponse = `[ { + "coalesce_name": "tal-test", + "reverse_range_stop": null, + "port": null, + "weight": null, + "description": null, + "status_text": "active", + "deleted": "", + "reverse_range_start": null, + "active": "1", + "id": 64694, + "reverse_network": null, + "external_table": "tidy_record", + "macro": 0, + "zone_type": 0, + "zone_name": "k8s.netic.dk", + "zone_id": 2861, + "namehelper": null, + "name": "tal-test", + "reverse_class": null, + "coalesce_macro_dest": "10.68.1.2", + "loc_name": "internal", + "modified_by": "ash", + "location_id": 1, + "modified_date": "2021-04-19 07:14:04", + "type": 0, + "zone_record_grouping": "A", + "destination": "10.68.1.2", + "extra_ip": 0, + "macro_name": null, + "tidy_record": 0, + "value": 0, + "ttl": 0, + "status": "0", + "customer_id": 0, + "zone_status": 0, + "type_name": "A", + "zone_record": 0, + "history": 0, + "nice_macro_name": null +},{ + "extra_ip": null, + "active": 0, + "loc_name": "all", + "weight": null, + "type_name": "NS", + "reverse_class": null, + "zone_status": "0", + "destination": "a.ns.netic.dk.", + "reverse_range_start": null, + "customer_id": null, + "macro": null, + "reverse_range_stop": null, + "name": ".", + "zone_id": 2861, + "tidy_record": 1, + "ttl": 0, + "modified_by": null, + "status": -1, + "macro_name": null, + "nice_macro_name": null, + "coalesce_name": ".", + "reverse_network": null, + "namehelper": null, + "type": 4, + "zone_record_grouping": 0, + "deleted": 1, + "zone_name": null, + "modified_date": null, + "zone_type": null, + "location_id": null, + "coalesce_macro_dest": null, + "port": null, + "status_text": null, + "zone_record": 1, + "value": null, + "id": null, + "history": null, + "external_table": null, + "description": "inherired from soa configuration" +}]` + +const findRecordResponse = `[ + { + "modified_date": "2021-09-13 10:06:07", + "macro_name": null, + "status": 0, + "extra_ip": null, + "name": "prod1-api.trifork.shared", + "status_text": "active", + "type_name": "A", + "zone_name": "k8s.netic.dk", + "active": 1, + "zone_record": 0, + "description": null, + "type": 0, + "modified_by": "api-terraform-shared-k8s", + "destination": "77.243.49.187", + "zone_status": 0, + "created_by": "api-terraform-shared-k8s", + "external_table": "tidy_record", + "record_fullname": "prod1-api.trifork.shared.k8s.netic.dk", + "id": 65377, + "zone_type": 0, + "coalesce_name": "prod1-api.trifork.shared", + "created_date": "2021-09-13 10:06:07", + "location_id": 0, + "loc_name": "all", + "port": null, + "ttl": 3600, + "tidy_record": 1, + "value": 0, + "namehelper": null, + "zone_id": 2861, + "customer_id": 0, + "weight": null, + "coalesce_macro_dest": "77.243.49.187", + "macro": 0, + "nice_macro_name": null, + "history": 0 + } +]` + +const listZonesResponse = `[ + { + "id": 2926, + "last_check_date": "2021-09-07 14:25:24", + "masters": null, + "alias_type": null, + "server_state": 0, + "authoritative_log": "NXDOMAIN: ASKED(ns-cloud-e4.googledomains.com ns-cloud-e2.googledomains.com ns-cloud-e3.googledomains.com ns-cloud-e1.googledomains.com)", + "class": null, + "forwarders": null, + "soa_ttl": null, + "is_private": null, + "soa_slave_refresh": null, + "inject_ns_enable": 1, + "dnssec_lastsign": null, + "zone_name": "hackerdays.trifork.dev", + "soa_slave_expiration": null, + "soa_max_caching": null, + "dnssec_parent_state": -1, + "provision_state": 1, + "type": 0, + "authoritative_state": 3, + "network": null, + "update": 0, + "type_has_records": 1, + "dnssec_monitor_enable": 0, + "created_date": "2021-09-07 14:25:22", + "dnssec_enable": 0, + "range_stop": null, + "dnssec_genkeys": 1, + "type_text": "regular", + "customer_id": 0, + "autofill_enable": null, + "name": "hackerdays.trifork.dev", + "namehelper": null, + "range_start": null, + "soa_record": null, + "autofill_template": null, + "dnssec_parent_log": null, + "status": 0, + "description": "Subdomain delegation for automated DNS @Hackerdays 2021", + "alias_id": null, + "soa_slave_retry": null, + "provision_log": null, + "alias_name": null, + "modified_date": "2021-10-01 15:59:44", + "modified_by": "api-letsencrypt-shared-k8s", + "allow_transfer": null, + "provision_date": "2021-10-01 15:59:45", + "soa_contact": null, + "parent_id": null, + "do_validate": 0, + "serial": 502 + }, + { + "provision_date": "2021-10-04 14:31:46", + "allow_transfer": null, + "serial": 624, + "parent_id": 279, + "do_validate": 0, + "soa_contact": null, + "provision_log": null, + "soa_slave_retry": null, + "alias_id": null, + "modified_by": "api-terraform-shared-k8s", + "modified_date": "2021-10-04 14:31:44", + "alias_name": null, + "dnssec_parent_log": null, + "autofill_template": null, + "soa_record": null, + "range_start": null, + "name": "k8s.netic.dk", + "namehelper": null, + "autofill_enable": null, + "status": 0, + "description": null, + "created_date": "2021-04-16 14:43:04", + "dnssec_monitor_enable": 0, + "type_has_records": 1, + "update": 0, + "dnssec_genkeys": 1, + "type_text": "regular", + "customer_id": 0, + "range_stop": null, + "dnssec_enable": 0, + "type": 0, + "provision_state": 1, + "dnssec_parent_state": -1, + "soa_slave_expiration": null, + "soa_max_caching": null, + "authoritative_state": 1, + "network": null, + "inject_ns_enable": 1, + "soa_slave_refresh": null, + "is_private": null, + "dnssec_lastsign": null, + "zone_name": "k8s.netic.dk", + "class": null, + "server_state": 0, + "authoritative_log": "NOERROR: ASKED(a.ns.netic.dk b.ns.netic.dk c.ns.netic.dk): NS(a.ns.netic.dk c.ns.netic.dk b.ns.netic.dk)", + "alias_type": null, + "masters": null, + "soa_ttl": null, + "forwarders": null, + "id": 2861, + "last_check_date": "2021-10-04 09:59:25" + }, + { + "alias_name": null, + "modified_by": "api-letsencrypt-wsus", + "modified_date": "2021-10-04 13:38:36", + "soa_slave_retry": null, + "alias_id": null, + "provision_log": "W: TXT-record _acme-challenge.resilient01.soc [internal] overlaps with zone-name. A sub-zone already exists and overlaps _acme-challenge.resilient01.soc. The correct would be to use _acme-challenge.resilient01 in zone soc.netic.dk (ID=2914).\nW: A/PTR-record dab.test [internal] overlaps with zone-name. A sub-zone already exists and overlaps dab.test. The correct would be to use dab in zone test.netic.dk (ID=802).\nW: A/PTR-record dc1-1-a2-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc1-1-a2-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc4-1-a5-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc4-1-a5-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record fbserver03.template [internal] overlaps with zone-name. A sub-zone already exists and overlaps fbserver03.template. The correct would be to use fbserver03 in zone template.netic.dk (ID=2537).\nW: A/PTR-record mediavault04-a.sos [internal] overlaps with zone-name. A sub-zone already exists and overlaps mediavault04-a.sos. The correct would be to use mediavault04-a in zone sos.netic.dk (ID=2889).\nW: A/PTR-record mediavault04-b.sos [internal] overlaps with zone-name. A sub-zone already exists and overlaps mediavault04-b.sos. The correct would be to use mediavault04-b in zone sos.netic.dk (ID=2889).\nW: TXT-record _acme-challenge.resilient01.soc [external] overlaps with zone-name. A sub-zone already exists and overlaps _acme-challenge.resilient01.soc. The correct would be to use _acme-challenge.resilient01 in zone soc.netic.dk (ID=2914).\nW: A/PTR-record dab.test [external] overlaps with zone-name. A sub-zone already exists and overlaps dab.test. The correct would be to use dab in zone test.netic.dk (ID=802).\nW: AAAA/PTR-record dc1-1-a2-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc1-1-a2-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc4-1-a5-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc4-1-a5-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).", + "do_validate": 0, + "parent_id": null, + "soa_contact": null, + "serial": 17830, + "provision_date": "2021-10-04 13:38:56", + "allow_transfer": null, + "dnssec_enable": 1, + "type_text": "regular", + "dnssec_genkeys": 0, + "customer_id": 0, + "range_stop": null, + "update": 0, + "created_date": "2004-09-20 15:24:56", + "type_has_records": 1, + "dnssec_monitor_enable": 1, + "status": 0, + "description": "Netic A/S, Aalborg, Denmark", + "range_start": null, + "namehelper": null, + "name": "netic.dk", + "autofill_enable": null, + "dnssec_parent_log": "", + "soa_record": null, + "autofill_template": null, + "zone_name": "netic.dk", + "dnssec_lastsign": "2021-10-04 13:38:56", + "is_private": null, + "inject_ns_enable": 1, + "soa_slave_refresh": null, + "authoritative_state": 1, + "network": null, + "soa_slave_expiration": null, + "soa_max_caching": null, + "type": 0, + "dnssec_parent_state": 1, + "provision_state": 2, + "last_check_date": "2021-10-04 07:58:18", + "id": 279, + "soa_ttl": null, + "forwarders": null, + "masters": null, + "authoritative_log": "NOERROR: ASKED(a.ns.netic.dk b.ns.netic.dk c.ns.netic.dk): NS(a.ns.netic.dk b.ns.netic.dk c.ns.netic.dk)", + "server_state": 0, + "class": null, + "alias_type": null + }, + { + "range_start": null, + "name": "netic.eu", + "autofill_enable": null, + "namehelper": null, + "dnssec_parent_log": null, + "autofill_template": null, + "soa_record": null, + "description": "", + "status": 0, + "update": 0, + "created_date": "2009-04-14 10:10:50", + "type_has_records": 0, + "dnssec_monitor_enable": 0, + "dnssec_enable": 0, + "dnssec_genkeys": 1, + "type_text": "alias", + "customer_id": 0, + "range_stop": null, + "provision_date": "2021-10-04 13:39:09", + "allow_transfer": null, + "parent_id": null, + "do_validate": 0, + "soa_contact": null, + "serial": 12925, + "soa_slave_retry": null, + "alias_id": 279, + "provision_log": "W: TXT-record _acme-challenge.resilient01.soc [internal] overlaps with zone-name. A sub-zone already exists and overlaps _acme-challenge.resilient01.soc. The correct would be to use _acme-challenge.resilient01 in zone soc.netic.dk (ID=2914).\nW: A/PTR-record dab.test [internal] overlaps with zone-name. A sub-zone already exists and overlaps dab.test. The correct would be to use dab in zone test.netic.dk (ID=802).\nW: A/PTR-record dc1-1-a2-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc1-1-a2-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc4-1-a5-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc4-1-a5-rt01.log [internal] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record fbserver03.template [internal] overlaps with zone-name. A sub-zone already exists and overlaps fbserver03.template. The correct would be to use fbserver03 in zone template.netic.dk (ID=2537).\nW: A/PTR-record mediavault04-a.sos [internal] overlaps with zone-name. A sub-zone already exists and overlaps mediavault04-a.sos. The correct would be to use mediavault04-a in zone sos.netic.dk (ID=2889).\nW: A/PTR-record mediavault04-b.sos [internal] overlaps with zone-name. A sub-zone already exists and overlaps mediavault04-b.sos. The correct would be to use mediavault04-b in zone sos.netic.dk (ID=2889).\nW: TXT-record _acme-challenge.resilient01.soc [external] overlaps with zone-name. A sub-zone already exists and overlaps _acme-challenge.resilient01.soc. The correct would be to use _acme-challenge.resilient01 in zone soc.netic.dk (ID=2914).\nW: A/PTR-record dab.test [external] overlaps with zone-name. A sub-zone already exists and overlaps dab.test. The correct would be to use dab in zone test.netic.dk (ID=802).\nW: AAAA/PTR-record dc1-1-a2-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc1-1-a2-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc1-1-a2-rt01.log. The correct would be to use dc1-1-a2-rt01 in zone log.netic.dk (ID=1822).\nW: A/PTR-record dc4-1-a5-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).\nW: AAAA/PTR-record dc4-1-a5-rt01.log [external] overlaps with zone-name. A sub-zone already exists and overlaps dc4-1-a5-rt01.log. The correct would be to use dc4-1-a5-rt01 in zone log.netic.dk (ID=1822).", + "alias_name": "netic.dk", + "modified_by": "mni", + "modified_date": "2021-10-04 13:38:36", + "masters": null, + "class": null, + "server_state": 0, + "authoritative_log": "NOERROR: ASKED(b.ns.netic.dk a.ns.netic.dk c.ns.netic.dk): NS(b.ns.netic.dk c.ns.netic.dk a.ns.netic.dk)", + "alias_type": 0, + "soa_ttl": null, + "forwarders": null, + "id": 1180, + "last_check_date": "2021-10-04 12:51:23", + "soa_max_caching": null, + "soa_slave_expiration": null, + "type": 2, + "provision_state": 2, + "dnssec_parent_state": -1, + "authoritative_state": 1, + "network": null, + "is_private": null, + "inject_ns_enable": 1, + "soa_slave_refresh": null, + "dnssec_lastsign": null, + "zone_name": "netic.eu" + } +]` + +const listRecordsResponse = `[ + { + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "macro_name": null, + "zone_status": 0, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-hotrod-app/hotrod", + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-hotrod-app/hotrod", + "external_table": "tidy_record", + "zone_record_grouping": "TXT", + "history": 0, + "type": 5, + "weight": null, + "zone_type": 0, + "location_id": 0, + "customer_id": 0, + "coalesce_name": "hotrod", + "port": null, + "tidy_record": 1, + "name": "hotrod", + "modified_date": "2021-09-23 10:54:03", + "reverse_network": null, + "value": 0, + "loc_name": "all", + "ttl": 0, + "status": "0", + "id": 65682, + "reverse_range_start": null, + "deleted": "", + "type_name": "TXT", + "status_text": "active", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "reverse_class": null, + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s" + }, + { + "coalesce_macro_dest": "77.243.49.244", + "zone_status": 0, + "macro_name": null, + "zone_id": 2926, + "zone_record": 1, + "reverse_range_stop": null, + "history": 1, + "zone_record_grouping": 0, + "external_table": "tidy_record", + "destination": "77.243.49.244", + "description": "Default ingress to prod2.trifork.dedicated.k8s.netic.dk", + "location_id": 0, + "zone_type": 0, + "weight": null, + "type": 0, + "port": null, + "tidy_record": 1, + "customer_id": 0, + "coalesce_name": ".", + "value": 0, + "reverse_network": null, + "modified_date": "2021-09-17 08:43:10", + "name": ".", + "reverse_range_start": null, + "id": 65293, + "ttl": 0, + "status": "0", + "loc_name": "all", + "deleted": "", + "modified_by": "tal", + "namehelper": null, + "extra_ip": null, + "nice_macro_name": null, + "active": "1", + "reverse_class": null, + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "status_text": "active", + "type_name": "A" + }, + { + "reverse_range_start": null, + "id": 65291, + "loc_name": "all", + "ttl": 0, + "status": "0", + "reverse_network": null, + "value": 0, + "modified_date": "2021-09-07 14:25:22", + "name": ".", + "extra_ip": null, + "modified_by": "tal", + "namehelper": null, + "active": "1", + "nice_macro_name": " NAMESERVER_C", + "reverse_class": null, + "status_text": "active", + "type_name": "NS", + "macro": 1, + "zone_name": "hackerdays.trifork.dev", + "deleted": "", + "zone_record_grouping": 0, + "history": 0, + "external_table": "tidy_record", + "description": "", + "destination": "c.ns.netic.dk.", + "macro_name": "$NAMESERVER_C$", + "coalesce_macro_dest": "$NAMESERVER_C$", + "zone_status": 0, + "zone_id": 2926, + "zone_record": 1, + "reverse_range_stop": null, + "port": null, + "tidy_record": 1, + "customer_id": 0, + "coalesce_name": ".", + "location_id": 0, + "weight": null, + "zone_type": 0, + "type": 4 + }, + { + "external_table": "tidy_record", + "description": null, + "destination": "b.ns.netic.dk.", + "zone_record_grouping": 0, + "history": 0, + "zone_record": 1, + "reverse_range_stop": null, + "macro_name": "$NAMESERVER_B$", + "zone_status": 0, + "coalesce_macro_dest": "$NAMESERVER_B$", + "zone_id": 2926, + "port": null, + "tidy_record": 1, + "customer_id": 0, + "coalesce_name": ".", + "weight": null, + "zone_type": 0, + "type": 4, + "location_id": 0, + "id": 65294, + "loc_name": "all", + "ttl": 0, + "status": "0", + "reverse_range_start": null, + "modified_date": "2021-09-07 14:25:22", + "name": ".", + "reverse_network": null, + "value": 0, + "reverse_class": null, + "status_text": "active", + "type_name": "NS", + "macro": 1, + "zone_name": "hackerdays.trifork.dev", + "extra_ip": null, + "namehelper": null, + "modified_by": "tal", + "active": "1", + "nice_macro_name": " NAMESERVER_B", + "deleted": "" + }, + { + "name": "angular", + "modified_date": "2021-09-24 13:21:02", + "reverse_network": null, + "value": 0, + "loc_name": "all", + "status": "0", + "ttl": 0, + "id": 66727, + "reverse_range_start": null, + "deleted": "", + "type_name": "TXT", + "status_text": "active", + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "reverse_class": null, + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "macro_name": null, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/angular-example-app/angular-example", + "zone_status": 0, + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/angular-example-app/angular-example", + "external_table": "tidy_record", + "zone_record_grouping": "TXT", + "history": 0, + "type": 5, + "weight": null, + "zone_type": 0, + "location_id": 0, + "coalesce_name": "angular", + "customer_id": 0, + "port": null, + "tidy_record": 1 + }, + { + "deleted": "", + "nice_macro_name": null, + "active": "1", + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "extra_ip": null, + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "status_text": "active", + "type_name": "A", + "reverse_class": null, + "value": 0, + "reverse_network": null, + "modified_date": "2021-10-01 11:29:56", + "name": "dev.demo", + "reverse_range_start": null, + "ttl": 0, + "status": "0", + "loc_name": "all", + "id": 66875, + "location_id": 0, + "type": 0, + "zone_type": 0, + "weight": null, + "customer_id": 0, + "coalesce_name": "dev.demo", + "port": null, + "tidy_record": 1, + "zone_id": 2926, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "macro_name": null, + "reverse_range_stop": null, + "zone_record": 0, + "history": 0, + "zone_record_grouping": "A", + "destination": "77.243.49.244", + "description": null, + "external_table": "tidy_record" + }, + { + "loc_name": "all", + "ttl": 0, + "status": "0", + "id": 66733, + "reverse_range_start": null, + "name": "grafana", + "modified_date": "2021-09-27 12:52:24", + "reverse_network": null, + "value": 0, + "status_text": "active", + "type_name": "TXT", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "reverse_class": null, + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "deleted": "", + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/trifork-monitoring-system/grafana", + "external_table": "tidy_record", + "zone_record_grouping": "TXT", + "history": 0, + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "macro_name": null, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/trifork-monitoring-system/grafana", + "zone_status": 0, + "customer_id": 0, + "coalesce_name": "grafana", + "port": null, + "tidy_record": 1, + "type": 5, + "weight": null, + "zone_type": 0, + "location_id": 0 + }, + { + "zone_record": 0, + "reverse_range_stop": null, + "macro_name": null, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "zone_id": 2926, + "external_table": "tidy_record", + "description": null, + "destination": "77.243.49.244", + "zone_record_grouping": "A", + "history": 0, + "weight": null, + "zone_type": 0, + "type": 0, + "location_id": 0, + "tidy_record": 1, + "port": null, + "customer_id": 0, + "coalesce_name": "hotrod", + "modified_date": "2021-09-23 10:54:01", + "name": "hotrod", + "reverse_network": null, + "value": 0, + "id": 65681, + "loc_name": "all", + "status": "0", + "ttl": 0, + "reverse_range_start": null, + "deleted": "", + "reverse_class": null, + "type_name": "A", + "status_text": "active", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "extra_ip": null, + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "active": "1", + "nice_macro_name": null + }, + { + "tidy_record": 1, + "port": null, + "coalesce_name": "sonarqube", + "customer_id": 0, + "zone_type": 0, + "weight": null, + "type": 0, + "location_id": 0, + "external_table": "tidy_record", + "destination": "77.243.49.244", + "description": null, + "history": 0, + "zone_record_grouping": "A", + "zone_record": 0, + "reverse_range_stop": null, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "macro_name": null, + "zone_id": 2926, + "reverse_class": null, + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "type_name": "A", + "status_text": "active", + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "extra_ip": null, + "nice_macro_name": null, + "active": "1", + "deleted": "", + "id": 65701, + "ttl": 0, + "status": "0", + "loc_name": "all", + "reverse_range_start": null, + "modified_date": "2021-09-23 13:17:46", + "name": "sonarqube", + "value": 0, + "reverse_network": null + }, + { + "weight": null, + "zone_type": 0, + "type": 5, + "location_id": 0, + "port": null, + "tidy_record": 1, + "customer_id": 0, + "coalesce_name": "dev.demo", + "zone_record": 0, + "reverse_range_stop": null, + "macro_name": null, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-demo-dev/hotrod", + "zone_status": 0, + "zone_id": 2926, + "external_table": "tidy_record", + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-demo-dev/hotrod", + "zone_record_grouping": "TXT", + "history": 0, + "deleted": "", + "reverse_class": null, + "type_name": "TXT", + "status_text": "active", + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "active": "1", + "nice_macro_name": null, + "modified_date": "2021-10-01 11:29:56", + "name": "dev.demo", + "reverse_network": null, + "value": 0, + "id": 66876, + "loc_name": "all", + "status": "0", + "ttl": 0, + "reverse_range_start": null + }, + { + "type": 0, + "weight": null, + "zone_type": 0, + "location_id": 0, + "coalesce_name": "angular", + "customer_id": 0, + "port": null, + "tidy_record": 1, + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "macro_name": null, + "coalesce_macro_dest": "77.243.49.244", + "zone_status": 0, + "description": null, + "destination": "77.243.49.244", + "external_table": "tidy_record", + "zone_record_grouping": "A", + "history": 0, + "deleted": "", + "type_name": "A", + "status_text": "active", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "reverse_class": null, + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "modified_date": "2021-09-24 13:21:02", + "name": "angular", + "reverse_network": null, + "value": 0, + "loc_name": "all", + "ttl": 0, + "status": "0", + "id": 66726, + "reverse_range_start": null + }, + { + "value": 0, + "reverse_network": null, + "modified_date": "2021-09-30 11:32:57", + "name": "springboot", + "reverse_range_start": null, + "ttl": 0, + "status": "0", + "loc_name": "all", + "id": 66865, + "deleted": "", + "nice_macro_name": null, + "active": "1", + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "extra_ip": null, + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "type_name": "TXT", + "status_text": "active", + "reverse_class": null, + "zone_id": 2926, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/spring-boot-example-app/spring-boot-example", + "zone_status": 0, + "macro_name": null, + "reverse_range_stop": null, + "zone_record": 0, + "history": 0, + "zone_record_grouping": "TXT", + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/spring-boot-example-app/spring-boot-example", + "description": null, + "external_table": "tidy_record", + "location_id": 0, + "type": 5, + "zone_type": 0, + "weight": null, + "coalesce_name": "springboot", + "customer_id": 0, + "port": null, + "tidy_record": 1 + }, + { + "reverse_network": null, + "value": 0, + "modified_date": "2021-09-23 13:17:46", + "name": "sonarqube", + "reverse_range_start": null, + "loc_name": "all", + "ttl": 0, + "status": "0", + "id": 65702, + "deleted": "", + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "status_text": "active", + "type_name": "TXT", + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "reverse_class": null, + "zone_id": 2926, + "macro_name": null, + "zone_status": 0, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/sonarqube/sonarqube", + "reverse_range_stop": null, + "zone_record": 0, + "zone_record_grouping": "TXT", + "history": 0, + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/sonarqube/sonarqube", + "external_table": "tidy_record", + "location_id": 0, + "type": 5, + "weight": null, + "zone_type": 0, + "customer_id": 0, + "coalesce_name": "sonarqube", + "port": null, + "tidy_record": 1 + }, + { + "modified_date": "2021-09-30 10:56:37", + "name": "podinfo", + "reverse_network": null, + "value": 0, + "loc_name": "all", + "ttl": 0, + "status": "0", + "id": 66862, + "reverse_range_start": null, + "deleted": "", + "status_text": "active", + "type_name": "TXT", + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "reverse_class": null, + "active": "1", + "nice_macro_name": null, + "extra_ip": null, + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "macro_name": null, + "zone_status": 0, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/podinfo-example-app/podinfo", + "description": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/podinfo-example-app/podinfo", + "external_table": "tidy_record", + "zone_record_grouping": "TXT", + "history": 0, + "type": 5, + "weight": null, + "zone_type": 0, + "location_id": 0, + "coalesce_name": "podinfo", + "customer_id": 0, + "port": null, + "tidy_record": 1 + }, + { + "port": null, + "tidy_record": 1, + "coalesce_name": "dev-demo", + "customer_id": 0, + "zone_type": 0, + "weight": null, + "type": 5, + "location_id": 0, + "external_table": "tidy_record", + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-demo-dev/hotrod", + "description": null, + "history": 0, + "zone_record_grouping": "TXT", + "zone_record": 0, + "reverse_range_stop": null, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/hackerdays-demo-dev/hotrod", + "zone_status": 0, + "macro_name": null, + "zone_id": 2926, + "reverse_class": null, + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "status_text": "active", + "type_name": "TXT", + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "extra_ip": null, + "nice_macro_name": null, + "active": "1", + "deleted": "", + "id": 66879, + "status": "0", + "ttl": 0, + "loc_name": "all", + "reverse_range_start": null, + "name": "dev-demo", + "modified_date": "2021-10-01 11:30:56", + "value": 0, + "reverse_network": null + }, + { + "reverse_range_stop": null, + "zone_record": 0, + "zone_id": 2926, + "coalesce_macro_dest": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/pvinddata/pvs-app-deny-actuator", + "zone_status": 0, + "macro_name": null, + "destination": "heritage=external-dns,external-dns/owner=prod2.trifork,external-dns/resource=ingress/pvinddata/pvs-app-deny-actuator", + "description": null, + "external_table": "tidy_record", + "history": 0, + "zone_record_grouping": "TXT", + "type": 5, + "zone_type": 0, + "weight": null, + "location_id": 0, + "customer_id": 0, + "coalesce_name": "pvi", + "tidy_record": 1, + "port": null, + "modified_date": "2021-10-01 15:59:29", + "name": "pvi", + "value": 0, + "reverse_network": null, + "ttl": 0, + "status": "0", + "loc_name": "all", + "id": 66884, + "reverse_range_start": null, + "deleted": "", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "status_text": "active", + "type_name": "TXT", + "reverse_class": null, + "nice_macro_name": null, + "active": "1", + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "extra_ip": null + }, + { + "external_table": "tidy_record", + "destination": "77.243.49.244", + "description": null, + "history": 0, + "zone_record_grouping": "A", + "zone_record": 0, + "reverse_range_stop": null, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "macro_name": null, + "zone_id": 2926, + "port": null, + "tidy_record": 1, + "customer_id": 0, + "coalesce_name": "pvi", + "zone_type": 0, + "weight": null, + "type": 0, + "location_id": 0, + "id": 66883, + "ttl": 0, + "status": "0", + "loc_name": "all", + "reverse_range_start": null, + "modified_date": "2021-10-01 15:59:29", + "name": "pvi", + "value": 0, + "reverse_network": null, + "reverse_class": null, + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "status_text": "active", + "type_name": "A", + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "extra_ip": null, + "nice_macro_name": null, + "active": "1", + "deleted": "" + }, + { + "port": null, + "tidy_record": 1, + "coalesce_name": "podinfo", + "customer_id": 0, + "weight": null, + "zone_type": 0, + "type": 0, + "location_id": 0, + "external_table": "tidy_record", + "description": null, + "destination": "77.243.49.244", + "zone_record_grouping": "A", + "history": 0, + "zone_record": 0, + "reverse_range_stop": null, + "macro_name": null, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "zone_id": 2926, + "reverse_class": null, + "status_text": "active", + "type_name": "A", + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "extra_ip": null, + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "active": "1", + "nice_macro_name": null, + "deleted": "", + "id": 66861, + "loc_name": "all", + "ttl": 0, + "status": "0", + "reverse_range_start": null, + "name": "podinfo", + "modified_date": "2021-09-30 10:56:37", + "reverse_network": null, + "value": 0 + }, + { + "history": 0, + "zone_record_grouping": "A", + "destination": "77.243.49.244", + "description": null, + "external_table": "tidy_record", + "zone_id": 2926, + "coalesce_macro_dest": "77.243.49.244", + "zone_status": 0, + "macro_name": null, + "reverse_range_stop": null, + "zone_record": 0, + "coalesce_name": "dev-demo", + "customer_id": 0, + "port": null, + "tidy_record": 1, + "location_id": 0, + "type": 0, + "zone_type": 0, + "weight": null, + "reverse_range_start": null, + "status": "0", + "ttl": 0, + "loc_name": "all", + "id": 66878, + "value": 0, + "reverse_network": null, + "name": "dev-demo", + "modified_date": "2021-10-01 11:30:56", + "nice_macro_name": null, + "active": "1", + "namehelper": null, + "modified_by": "api-letsencrypt-shared-k8s", + "extra_ip": null, + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "type_name": "A", + "status_text": "active", + "reverse_class": null, + "deleted": "" + }, + { + "reverse_range_start": null, + "id": 66864, + "ttl": 0, + "status": "0", + "loc_name": "all", + "value": 0, + "reverse_network": null, + "modified_date": "2021-09-30 11:32:57", + "name": "springboot", + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "extra_ip": null, + "nice_macro_name": null, + "active": "1", + "reverse_class": null, + "zone_name": "hackerdays.trifork.dev", + "macro": 0, + "type_name": "A", + "status_text": "active", + "deleted": "", + "history": 0, + "zone_record_grouping": "A", + "external_table": "tidy_record", + "destination": "77.243.49.244", + "description": null, + "coalesce_macro_dest": "77.243.49.244", + "zone_status": 0, + "macro_name": null, + "zone_id": 2926, + "zone_record": 0, + "reverse_range_stop": null, + "tidy_record": 1, + "port": null, + "customer_id": 0, + "coalesce_name": "springboot", + "location_id": 0, + "zone_type": 0, + "weight": null, + "type": 0 + }, + { + "location_id": 0, + "type": 0, + "zone_type": 0, + "weight": null, + "customer_id": 0, + "coalesce_name": "grafana", + "tidy_record": 1, + "port": null, + "zone_id": 2926, + "zone_status": 0, + "coalesce_macro_dest": "77.243.49.244", + "macro_name": null, + "reverse_range_stop": null, + "zone_record": 0, + "history": 0, + "zone_record_grouping": "A", + "destination": "77.243.49.244", + "description": null, + "external_table": "tidy_record", + "deleted": "", + "nice_macro_name": null, + "active": "1", + "modified_by": "api-letsencrypt-shared-k8s", + "namehelper": null, + "extra_ip": null, + "macro": 0, + "zone_name": "hackerdays.trifork.dev", + "status_text": "active", + "type_name": "A", + "reverse_class": null, + "value": 0, + "reverse_network": null, + "name": "grafana", + "modified_date": "2021-09-27 12:52:24", + "reverse_range_start": null, + "status": "0", + "ttl": 0, + "loc_name": "all", + "id": 66732 + }, + { + "coalesce_name": ".", + "customer_id": null, + "port": null, + "tidy_record": 1, + "type": 4, + "zone_type": null, + "weight": null, + "location_id": null, + "destination": "a.ns.netic.dk.", + "description": "inherired from soa configuration", + "external_table": null, + "history": null, + "zone_record_grouping": 0, + "reverse_range_stop": null, + "zone_record": 1, + "zone_id": 2926, + "coalesce_macro_dest": null, + "zone_status": "0", + "macro_name": null, + "zone_name": null, + "macro": null, + "type_name": "NS", + "status_text": null, + "reverse_class": null, + "nice_macro_name": null, + "active": 0, + "modified_by": null, + "namehelper": null, + "extra_ip": null, + "deleted": 1, + "status": -1, + "ttl": 0, + "loc_name": "all", + "id": null, + "reverse_range_start": null, + "name": ".", + "modified_date": null, + "value": null, + "reverse_network": null + } +]` diff --git a/pkg/tidydns/types.go b/pkg/tidydns/types.go new file mode 100644 index 0000000..e6bcc8f --- /dev/null +++ b/pkg/tidydns/types.go @@ -0,0 +1,28 @@ +package tidydns + +type zoneInfo struct { + ID int `json:"id"` + Name string `json:"name"` +} + +type recordRead struct { + ID int `json:"id"` + Type RecordType `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Destination string `json:"destination"` + TTL int `json:"ttl"` + Status RecordStatus `json:"status"` + Location LocationID `json:"location_id"` +} + +type recordList struct { + ID int `json:"id"` + Type RecordType `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Destination string `json:"destination"` + TTL int `json:"ttl"` + Status interface{} `json:"status"` + Location LocationID `json:"location_id"` +}