From 4ea702a549ef422afbe3f1f21a419c889879f0bb Mon Sep 17 00:00:00 2001 From: James Kassemi Date: Thu, 19 Dec 2013 11:07:06 -0700 Subject: [PATCH] Adds domains/records support --- README.md | 12 ---- domains.go | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ domains_test.go | 137 +++++++++++++++++++++++++++++++++++ 3 files changed, 325 insertions(+), 12 deletions(-) create mode 100644 domains.go create mode 100644 domains_test.go diff --git a/README.md b/README.md index 636a57c..f60b2b6 100644 --- a/README.md +++ b/README.md @@ -70,15 +70,3 @@ Most of the missing functionality should be straight to implement (best with som * show * edit * destroy - -### Domains - -* index -* new -* show -* destroy -* records -* records/new -* records/show -* records/edit -* records/destroy diff --git a/domains.go b/domains.go new file mode 100644 index 0000000..bb3e7e1 --- /dev/null +++ b/domains.go @@ -0,0 +1,188 @@ +package digo + +import ( + "errors" + "fmt" + "net/url" +) + +type DomainResponse struct { + Status string `json:"status"` + Domain *Domain `json:"domain"` +} + +type DomainsResponse struct { + Status string `json:"status"` + Domains []*Domain `json:"domains"` +} + +type DomainRecordResponse struct { + Status string `json:"status"` + DomainRecord *DomainRecord `json:"record"` +} + +type DomainRecordsResponse struct { + Status string `json:"status"` + DomainRecords []*DomainRecord `json:"records"` +} + +type Domain struct { + Id int `json:"id"` + Name string `json:"name"` + TTL int `json:"ttl"` + LiveZoneFile string `json:"live_zone_file"` + Error string `json:"error"` + ZoneFileWithError string `json:"zone_file_with_error"` + IpAddress string + account *Account +} + +type DomainRecord struct { + Id int `json:"id"` + DomainId int `json:"domain_id"` + RecordType string `json:"record_type"` + Name string `json:"name"` + Data string `json:"data"` + Priority int `json:"priority,omitempty"` + Port int `json:"port,omitempty"` + Weight int `json:"weight,omitempty"` + domain *Domain +} + +func (account *Account) Domains() ([]*Domain, error) { + r := &DomainsResponse{} + + if e := account.loadResource("/domains", r); e != nil { + return r.Domains, e + } + + for _, d := range r.Domains { + d.account = account + } + + return r.Domains, nil +} + +func (account *Account) Domain(id int) (*Domain, error) { + r := &DomainResponse{} + + if e := account.loadResource(fmt.Sprintf("/domains/%d", id), r); e != nil { + return nil, e + } + + r.Domain.account = account + + return r.Domain, nil +} + +func (account *Account) NewDomain() (*Domain, error) { + return &Domain{ + account: account, + }, nil +} + +func (domain *Domain) Save() error { + qs := &url.Values{} + + if domain.IpAddress == "" { + return errors.New("IP Address must not be blank") + } + + if domain.Name == "" { + return errors.New("Name must not be blank") + } + + qs.Set("name", domain.Name) + qs.Set("ip_address", domain.IpAddress) + + dr := &DomainResponse{} + + e := domain.account.loadResource(fmt.Sprintf("/domains/new?%s", qs.Encode()), dr) + + if e == nil { + domain.Id = dr.Domain.Id + } + + return e +} + +func (domain *Domain) Destroy() error { + return domain.account.loadResource(fmt.Sprintf("/domains/%d/destroy", domain.Id), &EventResponse{}) +} + +func (domain *Domain) Records() ([]*DomainRecord, error) { + r := &DomainRecordsResponse{} + + if e := domain.account.loadResource(fmt.Sprintf("/domains/%d/records", domain.Id), r); e != nil { + return r.DomainRecords, e + } + + for _, dr := range r.DomainRecords { + dr.domain = domain + } + + return r.DomainRecords, nil +} + +func (domain *Domain) Record(id int) (*DomainRecord, error) { + r := &DomainRecordResponse{} + + if e := domain.account.loadResource(fmt.Sprintf("/domains/%d/records/%d", domain.Id, id), r); e != nil { + return nil, e + } + + r.DomainRecord.domain = domain + + return r.DomainRecord, nil +} + +func (domain *Domain) NewRecord() (*DomainRecord, error) { + return &DomainRecord{ + domain: domain, + }, nil +} + +func (record *DomainRecord) Save() error { + qs := &url.Values{} + + qs.Set("domain_id", fmt.Sprintf("%d", record.DomainId)) + qs.Set("record_type", record.RecordType) + qs.Set("name", record.Name) + qs.Set("data", record.Data) + + if record.Priority > 0 { + qs.Set("priority", fmt.Sprintf("%d", record.Priority)) + } + + if record.Port > 0 { + qs.Set("port", fmt.Sprintf("%d", record.Port)) + } + + if record.Weight > 0 { + qs.Set("weight", fmt.Sprintf("%d", record.Weight)) + } + + drr := &DomainRecordResponse{} + + var e error + + if record.Id > 0 { + e = record.domain.account.loadResource(fmt.Sprintf("/domains/%d/records/%d/edit?%s", record.domain.Id, record.Id, qs.Encode()), drr) + + } else { + e = record.domain.account.loadResource(fmt.Sprintf("/domains/%d/records/new?%s", record.domain.Id, qs.Encode()), drr) + + } + + if e != nil { + return e + } + + record.Id = drr.DomainRecord.Id + + return nil +} + +func (record *DomainRecord) Destroy() error { + return record.domain.account.loadResource(fmt.Sprintf("/domains/%d/records/%d/destroy", record.domain.Id, record.Id), &EventResponse{}) +} diff --git a/domains_test.go b/domains_test.go new file mode 100644 index 0000000..2580fac --- /dev/null +++ b/domains_test.go @@ -0,0 +1,137 @@ +package digo + +import ( + "errors" + "os" + "testing" +) + +func getAccount() (*Account, error) { + apiKey := os.Getenv("DIGITAL_OCEAN_API_KEY") + clientId := os.Getenv("DIGITAL_OCEAN_CLIENT_ID") + + if apiKey == "" { + return nil, errors.New("Need DIGITAL_OCEAN_API_KEY") + } + + if clientId == "" { + return nil, errors.New("Need DIGITAL_OCEAN_CLIENT_ID") + } + + return &Account{ + ApiKey: apiKey, + ClientId: clientId, + }, nil +} + +func TestDomains(t *testing.T) { + account, e := getAccount() + + if e != nil { + t.Fatal(e.Error()) + } + + domain, _ := account.NewDomain() + domain.IpAddress = "1.2.3.4" + domain.Name = "newdomain-123456.com" + + if e := domain.Save(); e != nil { + t.Fatal("Could not create domain: " + e.Error()) + } + + domain, e = account.Domain(domain.Id) + + if e != nil { + t.Fatal("Could not find domain: " + e.Error()) + } + + list, e := account.Domains() + + if e != nil { + t.Fatal("Could not retrieve list of domains: " + e.Error()) + } + + found := false + + for _, d := range list { + if d.Id == domain.Id { + found = true + } + } + + if found == false { + t.Fatal("Could not find domain") + } + + if e := domain.Destroy(); e != nil { + t.Fatal("Could not destroy") + } + + list, _ = account.Domains() + + found = false + + for _, d := range list { + if d.Id == domain.Id { + found = true + } + } + + if found == true { + t.Fatal("Expected to not find example.com!") + } +} + +func TestDomainRecords(t *testing.T) { + account, e := getAccount() + + if e != nil { + t.Fatal(e.Error()) + } + + domain, _ := account.NewDomain() + domain.IpAddress = "1.2.3.4" + domain.Name = "newdomain-123456.com" + domain.Save() + + defaultRecords, e := domain.Records() + + if e != nil { + t.Fatal("Could not load record list: ", e.Error()) + } + + record, _ := domain.NewRecord() + record.Name = "sample" + record.Data = "1.2.3.4" + record.RecordType = "A" + + if e = record.Save(); e != nil { + t.Fatal("Could not create DNS record: " + e.Error()) + } + + record.Name = "sample2" + + if e := record.Save(); e != nil { + t.Fatal("Could not save update to record: " + e.Error()) + } + + if record, e := domain.Record(record.Id); e != nil { + t.Fatal("Could not load existing record: " + e.Error()) + } else if record.Name != "sample2" { + t.Fatal("Record name not expected!") + } + + if records, _ := domain.Records(); records == nil || len(records) != (len(defaultRecords)+1) { + t.Fatal("Did not have expected number of records!") + } + + if e = record.Destroy(); e != nil { + t.Fatal("Unable to delete DNS record: " + e.Error()) + } + + if records, _ := domain.Records(); len(records) != len(defaultRecords) { + t.Fatal("Did not have expected number of records!") + } + + domain.Destroy() +}