Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ZonesServices: add #58

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Changed
- ZoneKey: add, wrap users of string Zones
- Client.NewRequest: shallow-copy BaseURL to avoid retaining modifications

## [1.3.0] - 2017-02-28
Expand Down
2 changes: 1 addition & 1 deletion cmd/udns/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func main() {
}

k := udnssdk.RRSetKey{
Zone: zone,
Zone: udnssdk.ZoneKey(zone),
Type: typ,
Name: domain,
}
Expand Down
2 changes: 1 addition & 1 deletion event.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type EventInfoListDTO struct {

// EventKey collects the identifiers of an Event
type EventKey struct {
Zone string
Zone ZoneKey
Type string
Name string
GUID string
Expand Down
2 changes: 1 addition & 1 deletion notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type NotificationListDTO struct {

// NotificationKey collects the identifiers of an Notification
type NotificationKey struct {
Zone string
Zone ZoneKey
Type string
Name string
Email string
Expand Down
2 changes: 1 addition & 1 deletion probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ type ProbesService struct {

// ProbeKey collects the identifiers of a Probe
type ProbeKey struct {
Zone string
Zone ZoneKey
Name string
ID string
}
Expand Down
6 changes: 3 additions & 3 deletions rrset.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,22 +236,22 @@ type RRSet struct {

// RRSetListDTO wraps a list of RRSet resources
type RRSetListDTO struct {
ZoneName string `json:"zoneName"`
Zone ZoneKey `json:"zoneName"`
Rrsets []RRSet `json:"rrsets"`
Queryinfo QueryInfo `json:"queryInfo"`
Resultinfo ResultInfo `json:"resultInfo"`
}

// RRSetKey collects the identifiers of a Zone
type RRSetKey struct {
Zone string
Zone ZoneKey
Type string
Name string
}

// URI generates the URI for an RRSet
func (k RRSetKey) URI() string {
uri := fmt.Sprintf("zones/%s/rrsets", k.Zone)
uri := fmt.Sprintf("%s/rrsets", k.Zone.URI())
if k.Type != "" {
uri += fmt.Sprintf("/%v", k.Type)
if k.Name != "" {
Expand Down
4 changes: 4 additions & 0 deletions udnssdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type Client struct {
RRSets *RRSetsService
// Tasks API
Tasks *TasksService
// Zones API
Zones *ZonesService
}

// NewClient returns a new ultradns API client.
Expand All @@ -97,6 +99,7 @@ func NewClient(username, password, baseURL string) (*Client, error) {
c.Probes = &ProbesService{client: c}
c.RRSets = &RRSetsService{client: c}
c.Tasks = &TasksService{client: c}
c.Zones = &ZonesService{client: c}
return c, nil
}

Expand All @@ -120,6 +123,7 @@ func newStubClient(username, password, baseURL, clientID, clientSecret string) (
c.Probes = &ProbesService{client: c}
c.RRSets = &RRSetsService{client: c}
c.Tasks = &TasksService{client: c}
c.Zones = &ZonesService{client: c}
return c, nil
}

Expand Down
4 changes: 2 additions & 2 deletions udnssdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ import (
var (
testUsername = os.Getenv("ULTRADNS_USERNAME")
testPassword = os.Getenv("ULTRADNS_PASSWORD")
testDomain = os.Getenv("ULTRADNS_DOMAIN")
testDomain = ZoneKey(os.Getenv("ULTRADNS_DOMAIN"))
testHostname = os.Getenv("ULTRADNS_TEST_HOSTNAME")
testIP1 = os.Getenv("ULTRADNS_TEST_IP1")
testIP2 = os.Getenv("ULTRADNS_TEST_IP2")
testBaseURL = os.Getenv("ULTRADNS_BASEURL")
testQuery = os.Getenv("ULTRADNS_TEST_QUERY")
testProbeType = os.Getenv("ULTRADNS_TEST_PROBE_TYPE")
testProbeName = os.Getenv("ULTRADNS_TEST_PROBE_NAME")
testProbeDomain = os.Getenv("ULTRADNS_TEST_PROBE_DOMAIN")
testProbeDomain = ZoneKey(os.Getenv("ULTRADNS_TEST_PROBE_DOMAIN"))

testIPDPoolName = "testipdpool"
testIPDPoolAddress = "127.0.0.1"
Expand Down
248 changes: 248 additions & 0 deletions zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
package udnssdk

import (
"fmt"
"log"
"net/http"
"time"
)

// ZonesService provides access to the zones resources
type ZonesService struct {
client *Client
}

// Zone wraps a response (or item) from:
// /zone GET
// /zone/{name} GET
// AKA "Zone DTO"
type Zone struct {
Properties ZoneProperties `json:"properties"`
RestrictIPList []ZoneRestrictIP `json:"restrictIPList"`
PrimaryNameServers ZonePrimaryNameServers `json:"primaryNameServers"`
OriginalZoneName string `json:"originalZoneName"`
RegistrarInfo ZoneRegistrarInfo
TSig ZoneTSig `json:"tsig"`
NotifyAddresses []ZoneNotifyAddress `json:"notifyAddresses"`
}

// ZoneCreate wraps a requests to:
// /zone POST
// /zone/{name} PUT
// AKA "Zone Create DTO"
type ZoneCreate struct {
Properties ZoneProperties `json:"properties"`
PrimaryCreate ZonePrimaryCreate `json:"primaryCreateInfo"`
SecondaryCreate ZoneSecondaryCreate `json:"secondaryCreateInfo"`
AliasCreateInfo ZoneAliasCreate `json:"aliasCreateInfo"`
}

// ZoneProperties wraps the properties value of Zone and represents attributes common to all zones
// All values will be ignored if present in a Create or Update
// AKA "Zone Properties DTO"
type ZoneProperties struct {
Name ZoneKey `json:"name"`
AccountName string `json:"accountName"`
Owner string `json:"owner"`
Type string `json:"type"` // "ALIAS", "PRIMARY", "SECONDARY"
RecordCount int `json:"recordCount"`
DNSSecStatus string `json:"dnssecStatus"` // "SIGNED", "UNSIGNED"
LastModifiedDateTime time.Time `json:"lastModifiedDateTime"` // ISO8601/RFC3399
}

// ZonePrimaryCreate wraps the primaryCreateInfo value of the ZoneCreate
// AKA "Primary Zone DTO"
type ZonePrimaryCreate struct {
ForceImport bool `json:"forceImport"`
CreateType string `json:"createType"` // "NEW", "COPY", "TRANSFER", "UPLOAD"
NameServer ZoneNameServerIP `json:"nameServer"`
OriginalZoneName string `json:"originalZoneName"`
RestrictIPList []ZoneRestrictIP `json:"restrictIPList"`
TSig ZoneTSig `json:"tsig"`
NotifyAddresses []ZoneNotifyAddress `json:"notifyAddresses"`
Inheirit string `json:"inheirit"` // "ALL", "NONE", "IP_RANGE", "NOTIFY_IP", "TSIG", joined by ","
}

// ZoneRestrictIP wraps the restrictIPList value of a Zone and ZonePrimaryCreate
// AKA "Restrict IP DTOs"
// cf IPAddrRange
type ZoneRestrictIP struct {
Start string `json:"startIP"`
End string `json:"endIP"`
CIDR string `json:"cidr"`
Address string `json:"singleIP"`
Comment string `json:"comment"`
}

// ZoneSecondaryCreate wraps the secondaryCreateInfo value of ZoneCreate
// AKA "Secondary Zone DTO"
type ZoneSecondaryCreate struct {
PrimaryNameServers ZonePrimaryNameServers `json:"primaryNameServers"`
}

// ZoneAliasCreate wraps the aliasCreateInfo value of ZoneCreate
// AKA "Alias Zone DTO"
type ZoneAliasCreate struct {
OriginalZoneName string `json:"originalZoneName"`
}

// ZonePrimaryNameServers wraps the primaryNameServers value of a Zone and ZoneCreate
// AKA "Name Server IP List DTO"
type ZonePrimaryNameServers struct {
NameServerIPList ZoneNameServerIPList `json:"nameServerIpList"`
}

// ZoneNameServerIPList yes, this is really how the data is specified
type ZoneNameServerIPList struct {
NameServerIP1 ZoneNameServerIP `json:"nameServerIp1"`
NameServerIP2 ZoneNameServerIP `json:"nameServerIp2"`
NameServerIP3 ZoneNameServerIP `json:"nameServerIp3"`
}

// ZoneNameServerIP wraps the nameServerIp* values or ZoneNameServerIPList
type ZoneNameServerIP struct {
IP string `json:"ip"`
TSigKey string `json:"tsigKey"`
TSigKeyValue string `json:"tsigKeyValue"`
}

// ZoneRegistrarInfo wraps the registrarInfo of Zone
// AKA "Registrar Info DTO"
type ZoneRegistrarInfo struct {
Registrar string `json:"registrar"`
WhoisExpiration time.Time `json:"whoisExpiration"`
NameServers ZoneRegistrarNameServers `json:"nameServers"`
}

// ZoneRegistrarNameServers wraps the nameServers value of ZoneRegistrarInfo
type ZoneRegistrarNameServers struct {
Ok []string `json:"ok"`
Unknown []string `json:"unknown"`
Missing []string `json:"missing"`
Incorrect []string `json:"incorrect"`
}

// ZoneTSig was the tsig value of Zone and ZoneCreate
// AKA "TSIG DTO"
type ZoneTSig struct {
KeyName string `json:"tsigKeyName"`
KeyValue string `json:"tsigKeyValue"`
Description string `json:"description"`
Algorithm string `json:"tsigAlgorithm"`
}

// ZoneNotifyAddress wraps the notifyAddress value of Zone and ZonePrimaryCreate
// AKA "Notify Address DTO"
type ZoneNotifyAddress struct {
NotifyAddress string `json:"notifyAddress"`
Description string `json:"description"`
}

// ZoneKey is the key for an UltraDNS zone
type ZoneKey string

// URI generates the URI for a task
func (z ZoneKey) URI() string {
return fmt.Sprintf("zones/%s", z)
}

// ZonesQueryURI generates the query URI for the zone collection given a query and offset
func ZonesQueryURI(query string, offset int) string {
sort := "NAME"
limit := 100
reverse := false
if query != "" {
return fmt.Sprintf("zones?q=%v&offset=%d&limit=%d&sort=%v&reverse=%v", query, offset, limit, sort, reverse)
}
return fmt.Sprintf("zones?offset=%d&limit=%d&sort=%v&reverse=%v", offset, limit, sort, reverse)
}

// Select requests all zones, with pagination
func (s *ZonesService) Select(query string) ([]Zone, error) {
// TODO: Sane Configuration for timeouts / retries
maxerrs := 5
waittime := 5 * time.Second

// init accumulators
dtos := []Zone{}
offset := 0
errcnt := 0

for {
reqDtos, ri, res, err := s.SelectWithOffset(query, offset)
if err != nil {
if res != nil && res.StatusCode >= 500 {
errcnt = errcnt + 1
if errcnt < maxerrs {
time.Sleep(waittime)
continue
}
}
return dtos, err
}

log.Printf("[DEBUG] ResultInfo: %+v\n", ri)
for _, d := range reqDtos {
dtos = append(dtos, d)
}
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
return dtos, nil
}
offset = ri.ReturnedCount + ri.Offset
continue
}
}

// ZoneList wraps a response from:
// /zone?q= GET
// AKA "Zone List DTO"
type ZoneList struct {
Zones []Zone `json:"zones"`
QueryInfo QueryInfo `json:"queryInfo"`
ResultInfo ResultInfo `json:"resultInfo"`
}

// SelectWithOffset request zones by query & offset, list them also returning list metadata, the actual response, or an error
func (s *ZonesService) SelectWithOffset(query string, offset int) ([]Zone, ResultInfo, *http.Response, error) {
var zl ZoneList

uri := ZonesQueryURI(query, offset)
res, err := s.client.get(uri, &zl)

zs := []Zone{}
for _, z := range zl.Zones {
zs = append(zs, z)
}
return zs, zl.ResultInfo, res, err
}

// Find Get the properties of a zone.
func (s *ZonesService) Find(z ZoneKey) (Zone, *http.Response, error) {
var zv Zone
res, err := s.client.get(z.URI(), &zv)
return zv, res, err
}

// Create creates a zone with val
func (s *ZonesService) Create(z ZoneKey, val ZoneCreate) (*http.Response, error) {
var ignored interface{}
return s.client.post(z.URI(), val, &ignored)
}

// Update updates a Zone with the provided val
// Cannot be used to:
// - update an alias
// - specify primary name servers for a primary zone
// - specify IPs, TSig, Notify addresses for a secondary zone
func (s *ZonesService) Update(z ZoneKey, val ZoneCreate) (*http.Response, error) {
var ignored interface{}
return s.client.put(z.URI(), val, &ignored)
}

// Convert: secondary => primary
// Unalias: alias => primary

// Delete requests deletions
func (s *ZonesService) Delete(z ZoneKey) (*http.Response, error) {
return s.client.delete(z.URI(), nil)
}