From e4a3ca688d9ccdee92d76b59ed5e49559524b405 Mon Sep 17 00:00:00 2001 From: shadtimm Date: Thu, 7 Apr 2022 17:34:03 -0700 Subject: [PATCH] Adding full support for txt records (#151) Co-authored-by: Shad Timm <17091319+shadtimm@users.noreply.github.com> --- README.md | 3 + object_manager.go | 5 +- object_manager_test.go | 4 ++ object_manager_txt-record.go | 75 +++++++++++++------------ object_manager_txt-record_test.go | 91 ++++++++++++++++++++++--------- objects.go | 48 ++++++++++++---- objects_test.go | 18 +++--- 7 files changed, 159 insertions(+), 85 deletions(-) diff --git a/README.md b/README.md index 324fede2..937dfb5f 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,8 @@ This library is compatible with Go 1.2+ * GetNetworkViewByRef * GetPTRRecordByRef * GetPTRRecord + * GetTXTRecord + * GetTXTRecordByRef * GetZoneAuthByRef * GetZoneDelegated * GetUpgradeStatus (2.7 or above) @@ -125,6 +127,7 @@ This library is compatible with Go 1.2+ * UpdateNetworkContainer * UpdateNetworkView * UpdatePTRRecord + * UpdateTXTRecord * UpdateARecord * UpdateZoneDelegated diff --git a/object_manager.go b/object_manager.go index 38f4c67a..5d98e2b6 100644 --- a/object_manager.go +++ b/object_manager.go @@ -23,7 +23,7 @@ type IBObjectManager interface { CreateNetworkContainer(netview string, cidr string, isIPv6 bool, comment string, eas EA) (*NetworkContainer, error) CreateNetworkView(name string, comment string, setEas EA) (*NetworkView, error) CreatePTRRecord(networkView string, dnsView string, ptrdname string, recordName string, cidr string, ipAddr string, useTtl bool, ttl uint32, comment string, eas EA) (*RecordPTR, error) - CreateTXTRecord(recordname string, text string, ttl uint, dnsview string) (*RecordTXT, error) + CreateTXTRecord(dnsView string, recordName string, text string, ttl uint32, useTtl bool, comment string, eas EA) (*RecordTXT, error) CreateZoneDelegated(fqdn string, delegate_to []NameServer) (*ZoneDelegated, error) DeleteARecord(ref string) (string, error) DeleteAAAARecord(ref string) (string, error) @@ -58,6 +58,8 @@ type IBObjectManager interface { GetNetworkViewByRef(ref string) (*NetworkView, error) GetPTRRecord(dnsview string, ptrdname string, recordName string, ipAddr string) (*RecordPTR, error) GetPTRRecordByRef(ref string) (*RecordPTR, error) + GetTXTRecord(dnsview string, name string) (*RecordTXT, error) + GetTXTRecordByRef(ref string) (*RecordTXT, error) GetZoneAuthByRef(ref string) (*ZoneAuth, error) GetZoneDelegated(fqdn string) (*ZoneDelegated, error) GetCapacityReport(name string) ([]CapacityReport, error) @@ -74,6 +76,7 @@ type IBObjectManager interface { UpdateNetworkContainer(ref string, setEas EA, comment string) (*NetworkContainer, error) UpdateNetworkView(ref string, name string, comment string, setEas EA) (*NetworkView, error) UpdatePTRRecord(ref string, netview string, ptrdname string, name string, cidr string, ipAddr string, useTtl bool, ttl uint32, comment string, setEas EA) (*RecordPTR, error) + UpdateTXTRecord(ref string, recordName string, text string, ttl uint32, useTtl bool, comment string, eas EA) (*RecordTXT, error) UpdateARecord(ref string, name string, ipAddr string, cidr string, netview string, ttl uint32, useTTL bool, comment string, eas EA) (*RecordA, error) UpdateZoneDelegated(ref string, delegate_to []NameServer) (*ZoneDelegated, error) } diff --git a/object_manager_test.go b/object_manager_test.go index cd1b2a32..92ee6ac0 100644 --- a/object_manager_test.go +++ b/object_manager_test.go @@ -76,6 +76,8 @@ func (c *fakeConnector) GetObject(obj IBObject, ref string, qp *QueryParams, res *res.(*[]RecordAAAA) = c.resultObject.([]RecordAAAA) case *RecordPTR: *res.(*[]RecordPTR) = c.resultObject.([]RecordPTR) + case *RecordTXT: + *res.(*[]RecordTXT) = c.resultObject.([]RecordTXT) case *ZoneDelegated: *res.(*[]ZoneDelegated) = c.resultObject.([]ZoneDelegated) case *RecordCNAME: @@ -99,6 +101,8 @@ func (c *fakeConnector) GetObject(obj IBObject, ref string, qp *QueryParams, res **res.(**HostRecord) = *c.resultObject.(*HostRecord) case *RecordPTR: **res.(**RecordPTR) = *c.resultObject.(*RecordPTR) + case *RecordTXT: + **res.(**RecordTXT) = *c.resultObject.(*RecordTXT) case *RecordCNAME: **res.(**RecordCNAME) = *c.resultObject.(*RecordCNAME) case *RecordA: diff --git a/object_manager_txt-record.go b/object_manager_txt-record.go index 7bcf1210..95273b55 100644 --- a/object_manager_txt-record.go +++ b/object_manager_txt-record.go @@ -3,74 +3,77 @@ package ibclient import "fmt" // Creates TXT Record. Use TTL of 0 to inherit TTL from the Zone -func (objMgr *ObjectManager) CreateTXTRecord(recordname string, text string, ttl uint, dnsview string) (*RecordTXT, error) { +func (objMgr *ObjectManager) CreateTXTRecord( + dnsView string, + recordName string, + text string, + ttl uint32, + useTtl bool, + comment string, + eas EA) (*RecordTXT, error) { - recordTXT := NewRecordTXT(RecordTXT{ - View: dnsview, - Name: recordname, - Text: text, - Ttl: ttl, - }) + recordTXT := NewRecordTXT(dnsView, "", recordName, text, ttl, useTtl, comment, eas) ref, err := objMgr.connector.CreateObject(recordTXT) - recordTXT.Ref = ref + if err != nil { + return nil, err + } + recordTXT, err = objMgr.GetTXTRecordByRef(ref) return recordTXT, err } func (objMgr *ObjectManager) GetTXTRecordByRef(ref string) (*RecordTXT, error) { - recordTXT := NewRecordTXT(RecordTXT{}) + recordTXT := NewEmptyRecordTXT() err := objMgr.connector.GetObject( recordTXT, ref, NewQueryParams(false, nil), &recordTXT) return recordTXT, err } -func (objMgr *ObjectManager) GetTXTRecord(name string) (*RecordTXT, error) { - if name == "" { - return nil, fmt.Errorf("name can not be empty") +func (objMgr *ObjectManager) GetTXTRecord(dnsview string, name string) (*RecordTXT, error) { + if dnsview == "" || name == "" { + return nil, fmt.Errorf("DNS view and name are required to retrieve a unique txt record") } var res []RecordTXT - recordTXT := NewRecordTXT(RecordTXT{}) + recordTXT := NewEmptyRecordTXT() sf := map[string]string{ + "view": dnsview, "name": name, } queryParams := NewQueryParams(false, sf) err := objMgr.connector.GetObject(recordTXT, "", queryParams, &res) - if err != nil || res == nil || len(res) == 0 { + if err != nil { return nil, err + } else if res == nil || len(res) == 0 { + return nil, NewNotFoundError( + fmt.Sprintf( + "TXT record with name '%s' in DNS view '%s' is not found", + name, dnsview)) } - return &res[0], nil } -func (objMgr *ObjectManager) UpdateTXTRecord(recordname string, text string) (*RecordTXT, error) { - var res []RecordTXT - - recordTXT := NewRecordTXT(RecordTXT{Name: recordname}) - - sf := map[string]string{ - "name": recordname, - } - queryParams := NewQueryParams(false, sf) - err := objMgr.connector.GetObject(recordTXT, "", queryParams, &res) - - if len(res) == 0 { - return nil, nil - } - - res[0].Text = text +func (objMgr *ObjectManager) UpdateTXTRecord( + ref string, + recordName string, + text string, + ttl uint32, + useTtl bool, + comment string, + eas EA) (*RecordTXT, error) { - res[0].Zone = "" // set the Zone value to "" as its a non writable field - - _, err = objMgr.connector.UpdateObject(&res[0], res[0].Ref) + recordTXT := NewRecordTXT("", "", recordName, text, ttl, useTtl, comment, eas) + recordTXT.Ref = ref - if err != nil || res == nil || len(res) == 0 { + reference, err := objMgr.connector.UpdateObject(recordTXT, ref) + if err != nil { return nil, err } - return &res[0], nil + recordTXT, err = objMgr.GetTXTRecordByRef(reference) + return recordTXT, err } func (objMgr *ObjectManager) DeleteTXTRecord(ref string) (string, error) { diff --git a/object_manager_txt-record_test.go b/object_manager_txt-record_test.go index 97d26fcf..9d23e758 100644 --- a/object_manager_txt-record_test.go +++ b/object_manager_txt-record_test.go @@ -8,39 +8,25 @@ import ( ) var _ = Describe("Object Manager: TXT-record", func() { - Describe("Allocate TXT Record ", func() { + Describe("Create TXT Record ", func() { cmpType := "Docker" tenantID := "01234567890abcdef01234567890abcdef" - text := "test-text" dnsView := "default" + text := "test-text" recordName := "test" - ttl := uint(30) + useTtl := true + ttl := uint32(70) + comment := "creation test" + eas := EA{"Country": "test"} fakeRefReturn := fmt.Sprintf("record:txt/ZG5zLmJpbmRfY25h:%s/%20%20", recordName) aniFakeConnector := &fakeConnector{ - createObjectObj: NewRecordTXT(RecordTXT{ - Name: recordName, - Text: text, - Ttl: ttl, - View: dnsView, - }), - getObjectRef: fakeRefReturn, - getObjectObj: NewRecordTXT(RecordTXT{ - Name: recordName, - Text: text, - View: dnsView, - Ref: fakeRefReturn, - Ttl: ttl, - }), + createObjectObj: NewRecordTXT(dnsView, "", recordName, text, ttl, useTtl, comment, eas), + getObjectRef: fakeRefReturn, + getObjectObj: NewEmptyRecordTXT(), getObjectQueryParams: NewQueryParams(false, nil), - resultObject: NewRecordTXT(RecordTXT{ - Name: recordName, - Text: text, - View: dnsView, - Ttl: ttl, - Ref: fakeRefReturn, - }), - fakeRefReturn: fakeRefReturn, + resultObject: NewRecordTXT(dnsView, "", recordName, text, ttl, useTtl, comment, eas), + fakeRefReturn: fakeRefReturn, } objMgr := NewObjectManager(aniFakeConnector, cmpType, tenantID) @@ -48,7 +34,7 @@ var _ = Describe("Object Manager: TXT-record", func() { var actualRecord *RecordTXT var err error It("should pass expected TXT record Object to CreateObject", func() { - actualRecord, err = objMgr.CreateTXTRecord(recordName, text, 30, dnsView) + actualRecord, err = objMgr.CreateTXTRecord(dnsView, recordName, text, ttl, useTtl, comment, eas) }) It("should return expected TXT record Object", func() { Expect(actualRecord).To(Equal(aniFakeConnector.resultObject)) @@ -56,6 +42,59 @@ var _ = Describe("Object Manager: TXT-record", func() { }) }) + Describe("Update TXT record", func() { + var ( + err error + objMgr IBObjectManager + conn *fakeConnector + ref string + actualObj *RecordTXT + ) + + cmpType := "Docker" + tenantID := "01234567890abcdef01234567890abcdef" + recordName := "test" + + It("Updating text, ttl, useTtl, comment and EAs", func() { + ref = fmt.Sprintf("record:txt/ZG5zLmJpbmRfY25h:%s/%20%20", recordName) + initialEas := EA{"Country": "old value"} + initObj := NewRecordTXT("", "", recordName, "old-text", uint32(70), true, "old comment", initialEas) + initObj.Ref = ref + + expectedEas := EA{"Country": "new value"} + + updateText := "" + updateComment := "new comment" + updateUseTtl := true + updateTtl := uint32(10) + updatedRef := fmt.Sprintf("record:txt/ZG5zLmJpbmRfY25h:%s/%20%20", recordName) + updateObjIn := NewRecordTXT("", "", recordName, updateText, updateTtl, updateUseTtl, updateComment, expectedEas) + updateObjIn.Ref = ref + + expectedObj := NewRecordTXT("", "", recordName, updateText, updateTtl, updateUseTtl, updateComment, expectedEas) + expectedObj.Ref = updatedRef + + conn = &fakeConnector{ + getObjectObj: NewEmptyRecordTXT(), + getObjectQueryParams: NewQueryParams(false, nil), + getObjectRef: updatedRef, + getObjectError: nil, + resultObject: expectedObj, + + updateObjectObj: updateObjIn, + updateObjectRef: ref, + updateObjectError: nil, + + fakeRefReturn: updatedRef, + } + objMgr = NewObjectManager(conn, cmpType, tenantID) + + actualObj, err = objMgr.UpdateTXTRecord(ref, recordName, updateText, updateTtl, updateUseTtl, updateComment, expectedEas) + Expect(err).To(BeNil()) + Expect(*actualObj).To(BeEquivalentTo(*expectedObj)) + }) + }) + Describe("Delete TXT Record", func() { cmpType := "Docker" tenantID := "01234567890abcdef01234567890abcdef" diff --git a/objects.go b/objects.go index 123a66eb..9dd10751 100644 --- a/objects.go +++ b/objects.go @@ -729,25 +729,49 @@ func NewHostRecord( } type RecordTXT struct { - IBBase `json:"-"` - Ref string `json:"_ref,omitempty"` - Name string `json:"name,omitempty"` - Text string `json:"text,omitempty"` - Ttl uint `json:"ttl,omitempty"` - View string `json:"view,omitempty"` - Zone string `json:"zone,omitempty"` - Ea EA `json:"extattrs"` - UseTtl bool `json:"use_ttl"` + IBBase `json:"-"` + View string `json:"view,omitempty"` + Zone string `json:"zone,omitempty"` + Ref string `json:"_ref,omitempty"` + Name string `json:"name,omitempty"` + Text string `json:"text,omitempty"` + Ttl uint32 `json:"ttl"` + UseTtl bool `json:"use_ttl"` + Comment string `json:"comment"` + Ea EA `json:"extattrs"` } -func NewRecordTXT(rt RecordTXT) *RecordTXT { - res := rt +func NewEmptyRecordTXT() *RecordTXT { + res := RecordTXT{} res.objectType = "record:txt" - res.returnFields = []string{"extattrs", "name", "text", "view", "zone", "ttl", "use_ttl"} + res.returnFields = []string{"view", "zone", "name", "text", "ttl", "use_ttl", "comment", "extattrs"} return &res } +func NewRecordTXT( + dnsview string, + zone string, + recordname string, + text string, + ttl uint32, + useTtl bool, + comment string, + eas EA) *RecordTXT { + + res := NewEmptyRecordTXT() + res.View = dnsview + res.Zone = zone + res.Name = recordname + res.Text = text + res.Ttl = ttl + res.UseTtl = useTtl + res.Comment = comment + res.Ea = eas + + return res +} + type ZoneAuth struct { IBBase `json:"-"` Ref string `json:"_ref,omitempty"` diff --git a/objects_test.go b/objects_test.go index ae83912b..eeb645a2 100644 --- a/objects_test.go +++ b/objects_test.go @@ -604,27 +604,25 @@ var _ = Describe("Objects", func() { }) Context("RecordTXT object", func() { + view := "default" name := "txt.domain.com" text := "this is text string" - view := "default" - zone := "domain.com" + ttl := uint32(70) + useTtl := true + comment := "test client" + eas := EA{"Country": "test"} - rt := NewRecordTXT(RecordTXT{ - Name: name, - Text: text, - View: view, - Zone: zone}) + rt := NewRecordTXT(view, "", name, text, ttl, useTtl, comment, eas) It("should set fields correctly", func() { + Expect(rt.View).To(Equal(view)) Expect(rt.Name).To(Equal(name)) Expect(rt.Text).To(Equal(text)) - Expect(rt.View).To(Equal(view)) - Expect(rt.Zone).To(Equal(zone)) }) It("should set base fields correctly", func() { Expect(rt.ObjectType()).To(Equal("record:txt")) - Expect(rt.ReturnFields()).To(ConsistOf("extattrs", "name", "text", "view", "zone", "ttl", "use_ttl")) + Expect(rt.ReturnFields()).To(ConsistOf("view", "zone", "name", "text", "ttl", "use_ttl", "comment", "extattrs")) }) })