Merge pull request #5716 from Ensighten/ultradns-provider
This commit is contained in:
commit
66fdd196f1
|
@ -154,6 +154,10 @@
|
|||
"ImportPath": "github.com/DreamItGetIT/statuscake",
|
||||
"Rev": "8cbe86575f00210a6df2c19cb2f59b00cd181de3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/Ensighten/udnssdk",
|
||||
"Rev": "0290933f5e8afd933f2823fce32bf2847e6ea603"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/apparentlymart/go-cidr/cidr",
|
||||
"Rev": "a3ebdb999b831ecb6ab8a226e31b07b2b9061c47"
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/builtin/providers/ultradns"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
plugin.Serve(&plugin.ServeOpts{
|
||||
ProviderFunc: ultradns.Provider,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package ultradns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/Ensighten/udnssdk"
|
||||
)
|
||||
|
||||
// Config collects the connection service-endpoint and credentials
|
||||
type Config struct {
|
||||
Username string
|
||||
Password string
|
||||
BaseURL string
|
||||
}
|
||||
|
||||
// Client returns a new client for accessing UltraDNS.
|
||||
func (c *Config) Client() (*udnssdk.Client, error) {
|
||||
client, err := udnssdk.NewClient(c.Username, c.Password, c.BaseURL)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up client: %s", err)
|
||||
}
|
||||
|
||||
log.Printf("[INFO] UltraDNS Client configured for user: %s", client.Username)
|
||||
|
||||
return client, nil
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package ultradns
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
// Provider returns a terraform.ResourceProvider.
|
||||
func Provider() terraform.ResourceProvider {
|
||||
return &schema.Provider{
|
||||
Schema: map[string]*schema.Schema{
|
||||
"username": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ULTRADNS_USERNAME", nil),
|
||||
Description: "UltraDNS Username.",
|
||||
},
|
||||
|
||||
"password": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ULTRADNS_PASSWORD", nil),
|
||||
Description: "UltraDNS User Password",
|
||||
},
|
||||
"baseurl": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ULTRADNS_BASEURL", nil),
|
||||
Description: "UltraDNS Base Url(defaults to testing)",
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"ultradns_record": resourceUltraDNSRecord(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
}
|
||||
}
|
||||
|
||||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
config := Config{
|
||||
Username: d.Get("username").(string),
|
||||
Password: d.Get("password").(string),
|
||||
BaseURL: d.Get("baseurl").(string),
|
||||
}
|
||||
|
||||
return config.Client()
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package ultradns
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
var testAccProviders map[string]terraform.ResourceProvider
|
||||
var testAccProvider *schema.Provider
|
||||
|
||||
func init() {
|
||||
testAccProvider = Provider().(*schema.Provider)
|
||||
testAccProviders = map[string]terraform.ResourceProvider{
|
||||
"ultradns": testAccProvider,
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider(t *testing.T) {
|
||||
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvider_impl(t *testing.T) {
|
||||
var _ terraform.ResourceProvider = Provider()
|
||||
}
|
||||
|
||||
func testAccPreCheck(t *testing.T) {
|
||||
if v := os.Getenv("ULTRADNS_USERNAME"); v == "" {
|
||||
t.Fatal("ULTRADNS_USERNAME must be set for acceptance tests")
|
||||
}
|
||||
|
||||
if v := os.Getenv("ULTRADNS_PASSWORD"); v == "" {
|
||||
t.Fatal("ULTRADNS_PASSWORD must be set for acceptance tests")
|
||||
}
|
||||
|
||||
if v := os.Getenv("ULTRADNS_DOMAIN"); v == "" {
|
||||
t.Fatal("ULTRADNS_DOMAIN must be set for acceptance tests. The domain is used to create and destroy record against.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
package ultradns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Ensighten/udnssdk"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
type rRSetResource struct {
|
||||
OwnerName string
|
||||
RRType string
|
||||
RData []string
|
||||
TTL int
|
||||
Profile *udnssdk.StringProfile
|
||||
Zone string
|
||||
}
|
||||
|
||||
func newRRSetResource(d *schema.ResourceData) (rRSetResource, error) {
|
||||
r := rRSetResource{}
|
||||
|
||||
if attr, ok := d.GetOk("name"); ok {
|
||||
r.OwnerName = attr.(string)
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("type"); ok {
|
||||
r.RRType = attr.(string)
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("zone"); ok {
|
||||
r.Zone = attr.(string)
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("rdata"); ok {
|
||||
rdata := attr.([]interface{})
|
||||
r.RData = make([]string, len(rdata))
|
||||
for i, j := range rdata {
|
||||
r.RData[i] = j.(string)
|
||||
}
|
||||
}
|
||||
|
||||
if attr, ok := d.GetOk("ttl"); ok {
|
||||
r.TTL, _ = strconv.Atoi(attr.(string))
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r rRSetResource) RRSetKey() udnssdk.RRSetKey {
|
||||
return udnssdk.RRSetKey{
|
||||
Zone: r.Zone,
|
||||
Type: r.RRType,
|
||||
Name: r.OwnerName,
|
||||
}
|
||||
}
|
||||
|
||||
func (r rRSetResource) RRSet() udnssdk.RRSet {
|
||||
return udnssdk.RRSet{
|
||||
OwnerName: r.OwnerName,
|
||||
RRType: r.RRType,
|
||||
RData: r.RData,
|
||||
TTL: r.TTL,
|
||||
}
|
||||
}
|
||||
|
||||
func (r rRSetResource) ID() string {
|
||||
return fmt.Sprintf("%s.%s", r.OwnerName, r.Zone)
|
||||
}
|
||||
|
||||
func populateResourceDataFromRRSet(r udnssdk.RRSet, d *schema.ResourceData) error {
|
||||
zone := d.Get("zone")
|
||||
// ttl
|
||||
d.Set("ttl", r.TTL)
|
||||
// rdata
|
||||
err := d.Set("rdata", r.RData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ultradns_record.rdata set failed: %#v", err)
|
||||
}
|
||||
// hostname
|
||||
if r.OwnerName == "" {
|
||||
d.Set("hostname", zone)
|
||||
} else {
|
||||
if strings.HasSuffix(r.OwnerName, ".") {
|
||||
d.Set("hostname", r.OwnerName)
|
||||
} else {
|
||||
d.Set("hostname", fmt.Sprintf("%s.%s", r.OwnerName, zone))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceUltraDNSRecord() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceUltraDNSRecordCreate,
|
||||
Read: resourceUltraDNSRecordRead,
|
||||
Update: resourceUltraDNSRecordUpdate,
|
||||
Delete: resourceUltraDNSRecordDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
// Required
|
||||
"zone": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"type": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"rdata": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
// Optional
|
||||
"ttl": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "3600",
|
||||
},
|
||||
// Computed
|
||||
"hostname": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceUltraDNSRecordCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*udnssdk.Client)
|
||||
|
||||
r, err := newRRSetResource(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] ultradns_record create: %#v", r.RRSet())
|
||||
_, err = client.RRSets.Create(r.RRSetKey(), r.RRSet())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create UltraDNS RRSet: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(r.ID())
|
||||
log.Printf("[INFO] ultradns_record.id: %s", d.Id())
|
||||
|
||||
return resourceUltraDNSRecordRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceUltraDNSRecordRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*udnssdk.Client)
|
||||
|
||||
r, err := newRRSetResource(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rrsets, err := client.RRSets.Select(r.RRSetKey())
|
||||
if err != nil {
|
||||
uderr, ok := err.(*udnssdk.ErrorResponseList)
|
||||
if ok {
|
||||
for _, r := range uderr.Responses {
|
||||
// 70002 means Records Not Found
|
||||
if r.ErrorCode == 70002 {
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("ultradns_record not found: %s", err)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("ultradns_record not found: %s", err)
|
||||
}
|
||||
rec := rrsets[0]
|
||||
return populateResourceDataFromRRSet(rec, d)
|
||||
}
|
||||
|
||||
func resourceUltraDNSRecordUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*udnssdk.Client)
|
||||
|
||||
r, err := newRRSetResource(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] ultradns_record update: %#v", r.RRSet())
|
||||
_, err = client.RRSets.Update(r.RRSetKey(), r.RRSet())
|
||||
if err != nil {
|
||||
return fmt.Errorf("ultradns_record update failed: %s", err)
|
||||
}
|
||||
|
||||
return resourceUltraDNSRecordRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceUltraDNSRecordDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*udnssdk.Client)
|
||||
|
||||
r, err := newRRSetResource(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[INFO] ultradns_record delete: %#v", r.RRSet())
|
||||
_, err = client.RRSets.Delete(r.RRSetKey())
|
||||
if err != nil {
|
||||
return fmt.Errorf("ultradns_record delete failed: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
package ultradns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/Ensighten/udnssdk"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccUltraDNSRecord_Basic(t *testing.T) {
|
||||
var record udnssdk.RRSet
|
||||
domain := os.Getenv("ULTRADNS_DOMAIN")
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckUltraDNSRecordDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: fmt.Sprintf(testAccCheckUltraDNSRecordConfigBasic, domain),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckUltraDNSRecordExists("ultradns_record.foobar", &record),
|
||||
testAccCheckUltraDNSRecordAttributes(&record),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "name", "terraform"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "zone", domain),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "rdata.0", "192.168.0.10"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestAccUltraDNSRecord_Updated(t *testing.T) {
|
||||
var record udnssdk.RRSet
|
||||
domain := os.Getenv("ULTRADNS_DOMAIN")
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckUltraDNSRecordDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: fmt.Sprintf(testAccCheckUltraDNSRecordConfigBasic, domain),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckUltraDNSRecordExists("ultradns_record.foobar", &record),
|
||||
testAccCheckUltraDNSRecordAttributes(&record),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "name", "terraform"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "zone", domain),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "rdata.0", "192.168.0.10"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: fmt.Sprintf(testAccCheckUltraDNSRecordConfigNewValue, domain),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckUltraDNSRecordExists("ultradns_record.foobar", &record),
|
||||
testAccCheckUltraDNSRecordAttributesUpdated(&record),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "name", "terraform"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "zone", domain),
|
||||
resource.TestCheckResourceAttr(
|
||||
"ultradns_record.foobar", "rdata.0", "192.168.0.11"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckUltraDNSRecordDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*udnssdk.Client)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "ultradns_record" {
|
||||
continue
|
||||
}
|
||||
|
||||
k := udnssdk.RRSetKey{
|
||||
Zone: rs.Primary.Attributes["zone"],
|
||||
Name: rs.Primary.Attributes["name"],
|
||||
Type: rs.Primary.Attributes["type"],
|
||||
}
|
||||
|
||||
_, err := client.RRSets.Select(k)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("Record still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckUltraDNSRecordAttributes(record *udnssdk.RRSet) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
if record.RData[0] != "192.168.0.10" {
|
||||
return fmt.Errorf("Bad content: %v", record.RData)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckUltraDNSRecordAttributesUpdated(record *udnssdk.RRSet) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
if record.RData[0] != "192.168.0.11" {
|
||||
return fmt.Errorf("Bad content: %v", record.RData)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckUltraDNSRecordExists(n string, record *udnssdk.RRSet) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No Record ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*udnssdk.Client)
|
||||
k := udnssdk.RRSetKey{
|
||||
Zone: rs.Primary.Attributes["zone"],
|
||||
Name: rs.Primary.Attributes["name"],
|
||||
Type: rs.Primary.Attributes["type"],
|
||||
}
|
||||
|
||||
foundRecord, err := client.RRSets.Select(k)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if foundRecord[0].OwnerName != rs.Primary.Attributes["hostname"] {
|
||||
return fmt.Errorf("Record not found: %+v,\n %+v\n", foundRecord, rs.Primary.Attributes)
|
||||
}
|
||||
|
||||
*record = foundRecord[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccCheckUltraDNSRecordConfigBasic = `
|
||||
resource "ultradns_record" "foobar" {
|
||||
zone = "%s"
|
||||
|
||||
name = "terraform"
|
||||
rdata = [ "192.168.0.10" ]
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
}`
|
||||
|
||||
const testAccCheckUltraDNSRecordConfigNewValue = `
|
||||
resource "ultradns_record" "foobar" {
|
||||
zone = "%s"
|
||||
|
||||
name = "terraform"
|
||||
rdata = [ "192.168.0.11" ]
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
}`
|
|
@ -0,0 +1,67 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AccountsService provides access to account resources
|
||||
type AccountsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Account represents responses from the service
|
||||
type Account struct {
|
||||
AccountName string `json:"accountName"`
|
||||
AccountHolderUserName string `json:"accountHolderUserName"`
|
||||
OwnerUserName string `json:"ownerUserName"`
|
||||
NumberOfUsers int `json:"numberOfUsers"`
|
||||
NumberOfGroups int `json:"numberOfGroups"`
|
||||
AccountType string `json:"accountType"`
|
||||
}
|
||||
|
||||
// AccountListDTO represents a account index response
|
||||
type AccountListDTO struct {
|
||||
Accounts []Account `json:"accounts"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// AccountKey represents the string identifier of an Account
|
||||
type AccountKey string
|
||||
|
||||
// URI generates the URI for an Account
|
||||
func (k AccountKey) URI() string {
|
||||
uri := "accounts"
|
||||
if k != "" {
|
||||
uri = fmt.Sprintf("accounts/%s", k)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// AccountsURI generates the URI for Accounts collection
|
||||
func AccountsURI() string {
|
||||
return "accounts"
|
||||
}
|
||||
|
||||
// Select requests all Accounts of user
|
||||
func (s *AccountsService) Select() ([]Account, *Response, error) {
|
||||
var ald AccountListDTO
|
||||
res, err := s.client.get(AccountsURI(), &ald)
|
||||
|
||||
accts := []Account{}
|
||||
for _, t := range ald.Accounts {
|
||||
accts = append(accts, t)
|
||||
}
|
||||
return accts, res, err
|
||||
}
|
||||
|
||||
// Find requests an Account by AccountKey
|
||||
func (s *AccountsService) Find(k AccountKey) (Account, *Response, error) {
|
||||
var t Account
|
||||
res, err := s.client.get(k.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Delete requests deletion of an Account by AccountKey
|
||||
func (s *AccountsService) Delete(k AccountKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AlertsService manages Alerts
|
||||
type AlertsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ProbeAlertDataDTO wraps a probe alert response
|
||||
type ProbeAlertDataDTO struct {
|
||||
PoolRecord string `json:"poolRecord"`
|
||||
ProbeType string `json:"probeType"`
|
||||
ProbeStatus string `json:"probeStatus"`
|
||||
AlertDate time.Time `json:"alertDate"`
|
||||
FailoverOccured bool `json:"failoverOccured"`
|
||||
OwnerName string `json:"ownerName"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// ProbeAlertDataListDTO wraps the response for an index of probe alerts
|
||||
type ProbeAlertDataListDTO struct {
|
||||
Alerts []ProbeAlertDataDTO `json:"alerts"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// Select returns all probe alerts with a RRSetKey
|
||||
func (s *AlertsService) Select(k RRSetKey) ([]ProbeAlertDataDTO, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
as := []ProbeAlertDataDTO{}
|
||||
offset := 0
|
||||
errcnt := 0
|
||||
|
||||
for {
|
||||
reqAlerts, ri, res, err := s.SelectWithOffset(k, offset)
|
||||
if err != nil {
|
||||
if res.StatusCode >= 500 {
|
||||
errcnt = errcnt + 1
|
||||
if errcnt < maxerrs {
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return as, err
|
||||
}
|
||||
|
||||
log.Printf("ResultInfo: %+v\n", ri)
|
||||
for _, a := range reqAlerts {
|
||||
as = append(as, a)
|
||||
}
|
||||
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
|
||||
return as, nil
|
||||
}
|
||||
offset = ri.ReturnedCount + ri.Offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset returns the probe alerts with a RRSetKey, accepting an offset
|
||||
func (s *AlertsService) SelectWithOffset(k RRSetKey, offset int) ([]ProbeAlertDataDTO, ResultInfo, *Response, error) {
|
||||
var ald ProbeAlertDataListDTO
|
||||
|
||||
uri := k.AlertsQueryURI(offset)
|
||||
res, err := s.client.get(uri, &ald)
|
||||
|
||||
as := []ProbeAlertDataDTO{}
|
||||
for _, a := range ald.Alerts {
|
||||
as = append(as, a)
|
||||
}
|
||||
return as, ald.Resultinfo, res, err
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package udnssdk
|
||||
|
||||
// GetResultByURI just requests a URI
|
||||
func (c *Client) GetResultByURI(uri string) (*Response, error) {
|
||||
req, err := c.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := c.HTTPClient.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return &Response{Response: res}, err
|
||||
}
|
||||
return &Response{Response: res}, err
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DirectionalPoolsService manages 'account level' 'geo' and 'ip' groups for directional-pools
|
||||
type DirectionalPoolsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// DirectionalPool wraps an account-level directional-groups response from a index request
|
||||
type DirectionalPool struct {
|
||||
DirectionalPoolID string `json:"taskId"`
|
||||
DirectionalPoolStatusCode string `json:"taskStatusCode"`
|
||||
Message string `json:"message"`
|
||||
ResultURI string `json:"resultUri"`
|
||||
}
|
||||
|
||||
// AccountLevelGeoDirectionalGroupDTO wraps an account-level, geo directonal-group response
|
||||
type AccountLevelGeoDirectionalGroupDTO struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
// IPAddrDTO wraps an IP address range or CIDR block
|
||||
type IPAddrDTO struct {
|
||||
Start string `json:"start,omitempty"`
|
||||
End string `json:"end,omitempty"`
|
||||
CIDR string `json:"cidr,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
// AccountLevelIPDirectionalGroupDTO wraps an account-level, IP directional-group response
|
||||
type AccountLevelIPDirectionalGroupDTO struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
IPs []IPAddrDTO `json:"ips"`
|
||||
}
|
||||
|
||||
// DirectionalPoolListDTO wraps a list of account-level directional-groups response from a index request
|
||||
type DirectionalPoolListDTO struct {
|
||||
DirectionalPools []DirectionalPool `json:"tasks"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// AccountLevelGeoDirectionalGroupListDTO wraps a list of account-level, geo directional-groups response from a index request
|
||||
type AccountLevelGeoDirectionalGroupListDTO struct {
|
||||
AccountName string `json:"zoneName"`
|
||||
GeoGroups []AccountLevelGeoDirectionalGroupDTO `json:"geoGroups"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// AccountLevelIPDirectionalGroupListDTO wraps an account-level, IP directional-group response
|
||||
type AccountLevelIPDirectionalGroupListDTO struct {
|
||||
AccountName string `json:"zoneName"`
|
||||
IPGroups []AccountLevelIPDirectionalGroupDTO `json:"ipGroups"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// Geos allows access to the Geo DirectionalPools API
|
||||
func (s *DirectionalPoolsService) Geos() *GeoDirectionalPoolsService {
|
||||
return &GeoDirectionalPoolsService{client: s.client}
|
||||
}
|
||||
|
||||
// IPs allows access to the IP DirectionalPools API
|
||||
func (s *DirectionalPoolsService) IPs() *IPDirectionalPoolsService {
|
||||
return &IPDirectionalPoolsService{client: s.client}
|
||||
}
|
||||
|
||||
// DirectionalPoolKey collects the identifiers of a DirectionalPool
|
||||
type DirectionalPoolKey struct {
|
||||
Account AccountKey
|
||||
Type string
|
||||
Name string
|
||||
}
|
||||
|
||||
// URI generates the URI for directional pools by account, type & slug ID
|
||||
func (k DirectionalPoolKey) URI() string {
|
||||
if k.Name == "" {
|
||||
return fmt.Sprintf("%s/dirgroups/%s", k.Account.URI(), k.Type)
|
||||
}
|
||||
return fmt.Sprintf("%s/dirgroups/%s/%s", k.Account.URI(), k.Type, k.Name)
|
||||
}
|
||||
|
||||
// QueryURI generates the URI for directional pools by account, type, query & offset
|
||||
func (k DirectionalPoolKey) QueryURI(query string, offset int) string {
|
||||
uri := k.URI()
|
||||
|
||||
if query != "" {
|
||||
uri = fmt.Sprintf("%s?sort=NAME&query=%s&offset=%d", uri, query, offset)
|
||||
} else {
|
||||
uri = fmt.Sprintf("%s?offset=%d", uri, offset)
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
// GeoDirectionalPoolKey collects the identifiers of an DirectionalPool with type Geo
|
||||
type GeoDirectionalPoolKey struct {
|
||||
Account AccountKey
|
||||
Name string
|
||||
}
|
||||
|
||||
// DirectionalPoolKey generates the DirectionalPoolKey for the GeoDirectionalPoolKey
|
||||
func (k GeoDirectionalPoolKey) DirectionalPoolKey() DirectionalPoolKey {
|
||||
return DirectionalPoolKey{
|
||||
Account: k.Account,
|
||||
Type: "geo",
|
||||
Name: k.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// URI generates the URI for a GeoDirectionalPool
|
||||
func (k GeoDirectionalPoolKey) URI() string {
|
||||
return k.DirectionalPoolKey().URI()
|
||||
}
|
||||
|
||||
// QueryURI generates the GeoDirectionalPool URI with query
|
||||
func (k GeoDirectionalPoolKey) QueryURI(query string, offset int) string {
|
||||
return k.DirectionalPoolKey().QueryURI(query, offset)
|
||||
}
|
||||
|
||||
// GeoDirectionalPoolsService manages 'geo' groups for directional-pools
|
||||
type GeoDirectionalPoolsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Select requests all geo directional-pools, by query and account, providing pagination and error handling
|
||||
func (s *GeoDirectionalPoolsService) Select(k GeoDirectionalPoolKey, query string) ([]AccountLevelGeoDirectionalGroupDTO, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
dtos := []AccountLevelGeoDirectionalGroupDTO{}
|
||||
errcnt := 0
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
reqDtos, ri, res, err := s.SelectWithOffset(k, query, offset)
|
||||
if err != nil {
|
||||
if 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
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset requests list of geo directional-pools, by query & account, and an offset, returning the directional-group, the list-metadata, the actual response, or an error
|
||||
func (s *GeoDirectionalPoolsService) SelectWithOffset(k GeoDirectionalPoolKey, query string, offset int) ([]AccountLevelGeoDirectionalGroupDTO, ResultInfo, *Response, error) {
|
||||
var tld AccountLevelGeoDirectionalGroupListDTO
|
||||
|
||||
res, err := s.client.get(k.QueryURI(query, offset), &tld)
|
||||
|
||||
pis := []AccountLevelGeoDirectionalGroupDTO{}
|
||||
for _, pi := range tld.GeoGroups {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
return pis, tld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Find requests a geo directional-pool by name & account
|
||||
func (s *GeoDirectionalPoolsService) Find(k GeoDirectionalPoolKey) (AccountLevelGeoDirectionalGroupDTO, *Response, error) {
|
||||
var t AccountLevelGeoDirectionalGroupDTO
|
||||
res, err := s.client.get(k.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Create requests creation of a DirectionalPool by DirectionalPoolKey given a directional-pool
|
||||
func (s *GeoDirectionalPoolsService) Create(k GeoDirectionalPoolKey, val interface{}) (*Response, error) {
|
||||
return s.client.post(k.URI(), val, nil)
|
||||
}
|
||||
|
||||
// Update requests update of a DirectionalPool by DirectionalPoolKey given a directional-pool
|
||||
func (s *GeoDirectionalPoolsService) Update(k GeoDirectionalPoolKey, val interface{}) (*Response, error) {
|
||||
return s.client.put(k.URI(), val, nil)
|
||||
}
|
||||
|
||||
// Delete requests deletion of a DirectionalPool
|
||||
func (s *GeoDirectionalPoolsService) Delete(k GeoDirectionalPoolKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
||||
|
||||
// IPDirectionalPoolKey collects the identifiers of an DirectionalPool with type IP
|
||||
type IPDirectionalPoolKey struct {
|
||||
Account AccountKey
|
||||
Name string
|
||||
}
|
||||
|
||||
// DirectionalPoolKey generates the DirectionalPoolKey for the IPDirectionalPoolKey
|
||||
func (k IPDirectionalPoolKey) DirectionalPoolKey() DirectionalPoolKey {
|
||||
return DirectionalPoolKey{
|
||||
Account: k.Account,
|
||||
Type: "ip",
|
||||
Name: k.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// URI generates the IPDirectionalPool query URI
|
||||
func (k IPDirectionalPoolKey) URI() string {
|
||||
return k.DirectionalPoolKey().URI()
|
||||
}
|
||||
|
||||
// QueryURI generates the IPDirectionalPool URI with query
|
||||
func (k IPDirectionalPoolKey) QueryURI(query string, offset int) string {
|
||||
return k.DirectionalPoolKey().QueryURI(query, offset)
|
||||
}
|
||||
|
||||
// IPDirectionalPoolsService manages 'geo' groups for directional-pools
|
||||
type IPDirectionalPoolsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Select requests all IP directional-pools, using pagination and error handling
|
||||
func (s *IPDirectionalPoolsService) Select(k IPDirectionalPoolKey, query string) ([]AccountLevelIPDirectionalGroupDTO, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
gs := []AccountLevelIPDirectionalGroupDTO{}
|
||||
errcnt := 0
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
reqIPGroups, ri, res, err := s.SelectWithOffset(k, query, offset)
|
||||
if err != nil {
|
||||
if res.StatusCode >= 500 {
|
||||
errcnt = errcnt + 1
|
||||
if errcnt < maxerrs {
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return gs, err
|
||||
}
|
||||
|
||||
log.Printf("ResultInfo: %+v\n", ri)
|
||||
for _, g := range reqIPGroups {
|
||||
gs = append(gs, g)
|
||||
}
|
||||
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
|
||||
return gs, nil
|
||||
}
|
||||
offset = ri.ReturnedCount + ri.Offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset requests all IP directional-pools, by query & account, and an offset, returning the list of IP groups, list metadata & the actual response, or an error
|
||||
func (s *IPDirectionalPoolsService) SelectWithOffset(k IPDirectionalPoolKey, query string, offset int) ([]AccountLevelIPDirectionalGroupDTO, ResultInfo, *Response, error) {
|
||||
var tld AccountLevelIPDirectionalGroupListDTO
|
||||
|
||||
res, err := s.client.get(k.QueryURI(query, offset), &tld)
|
||||
|
||||
pis := []AccountLevelIPDirectionalGroupDTO{}
|
||||
for _, pi := range tld.IPGroups {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
|
||||
return pis, tld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Find requests a directional-pool by name & account
|
||||
func (s *IPDirectionalPoolsService) Find(k IPDirectionalPoolKey) (AccountLevelIPDirectionalGroupDTO, *Response, error) {
|
||||
var t AccountLevelIPDirectionalGroupDTO
|
||||
res, err := s.client.get(k.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Create requests creation of a DirectionalPool by DirectionalPoolKey given a directional-pool
|
||||
func (s *IPDirectionalPoolsService) Create(k IPDirectionalPoolKey, val interface{}) (*Response, error) {
|
||||
return s.client.post(k.URI(), val, nil)
|
||||
}
|
||||
|
||||
// Update requests update of a DirectionalPool by DirectionalPoolKey given a directional-pool
|
||||
func (s *IPDirectionalPoolsService) Update(k IPDirectionalPoolKey, val interface{}) (*Response, error) {
|
||||
return s.client.put(k.URI(), val, nil)
|
||||
}
|
||||
|
||||
// Delete deletes an directional-pool
|
||||
func (s *IPDirectionalPoolsService) Delete(k IPDirectionalPoolKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EventsService manages Events
|
||||
type EventsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// EventInfoDTO wraps an event's info response
|
||||
type EventInfoDTO struct {
|
||||
ID string `json:"id"`
|
||||
PoolRecord string `json:"poolRecord"`
|
||||
EventType string `json:"type"`
|
||||
Start time.Time `json:"start"`
|
||||
Repeat string `json:"repeat"`
|
||||
End time.Time `json:"end"`
|
||||
Notify string `json:"notify"`
|
||||
}
|
||||
|
||||
// EventInfoListDTO wraps a list of event info and list metadata, from an index request
|
||||
type EventInfoListDTO struct {
|
||||
Events []EventInfoDTO `json:"events"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// EventKey collects the identifiers of an Event
|
||||
type EventKey struct {
|
||||
Zone string
|
||||
Type string
|
||||
Name string
|
||||
GUID string
|
||||
}
|
||||
|
||||
// RRSetKey generates the RRSetKey for the EventKey
|
||||
func (p EventKey) RRSetKey() RRSetKey {
|
||||
return RRSetKey{
|
||||
Zone: p.Zone,
|
||||
Type: p.Type,
|
||||
Name: p.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// URI generates the URI for a probe
|
||||
func (p EventKey) URI() string {
|
||||
return fmt.Sprintf("%s/%s", p.RRSetKey().EventsURI(), p.GUID)
|
||||
}
|
||||
|
||||
// Select requests all events, using pagination and error handling
|
||||
func (s *EventsService) Select(r RRSetKey, query string) ([]EventInfoDTO, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
pis := []EventInfoDTO{}
|
||||
offset := 0
|
||||
errcnt := 0
|
||||
|
||||
for {
|
||||
reqEvents, ri, res, err := s.SelectWithOffset(r, query, offset)
|
||||
if err != nil {
|
||||
if res.StatusCode >= 500 {
|
||||
errcnt = errcnt + 1
|
||||
if errcnt < maxerrs {
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return pis, err
|
||||
}
|
||||
|
||||
log.Printf("ResultInfo: %+v\n", ri)
|
||||
for _, pi := range reqEvents {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
|
||||
return pis, nil
|
||||
}
|
||||
offset = ri.ReturnedCount + ri.Offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset requests list of events by RRSetKey, query and offset, also returning list metadata, the actual response, or an error
|
||||
func (s *EventsService) SelectWithOffset(r RRSetKey, query string, offset int) ([]EventInfoDTO, ResultInfo, *Response, error) {
|
||||
var tld EventInfoListDTO
|
||||
|
||||
uri := r.EventsQueryURI(query, offset)
|
||||
res, err := s.client.get(uri, &tld)
|
||||
|
||||
pis := []EventInfoDTO{}
|
||||
for _, pi := range tld.Events {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
return pis, tld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Find requests an event by name, type, zone & guid, also returning the actual response, or an error
|
||||
func (s *EventsService) Find(e EventKey) (EventInfoDTO, *Response, error) {
|
||||
var t EventInfoDTO
|
||||
res, err := s.client.get(e.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Create requests creation of an event by RRSetKey, with provided event-info, returning actual response or an error
|
||||
func (s *EventsService) Create(r RRSetKey, ev EventInfoDTO) (*Response, error) {
|
||||
return s.client.post(r.EventsURI(), ev, nil)
|
||||
}
|
||||
|
||||
// Update requests update of an event by EventKey, withprovided event-info, returning the actual response or an error
|
||||
func (s *EventsService) Update(e EventKey, ev EventInfoDTO) (*Response, error) {
|
||||
return s.client.put(e.URI(), ev, nil)
|
||||
}
|
||||
|
||||
// Delete requests deletion of an event by EventKey, returning the actual response or an error
|
||||
func (s *EventsService) Delete(e EventKey) (*Response, error) {
|
||||
return s.client.delete(e.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// NotificationsService manages Probes
|
||||
type NotificationsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// NotificationDTO manages notifications
|
||||
type NotificationDTO struct {
|
||||
Email string `json:"email"`
|
||||
PoolRecords []NotificationPoolRecord `json:"poolRecords"`
|
||||
}
|
||||
|
||||
// NotificationPoolRecord does things unknown
|
||||
type NotificationPoolRecord struct {
|
||||
PoolRecord string `json:"poolRecord"`
|
||||
Notification NotificationInfoDTO `json:"notification"`
|
||||
}
|
||||
|
||||
// NotificationInfoDTO does things unknown
|
||||
type NotificationInfoDTO struct {
|
||||
Probe bool `json:"probe"`
|
||||
Record bool `json:"record"`
|
||||
Scheduled bool `json:"scheduled"`
|
||||
}
|
||||
|
||||
// NotificationListDTO does things unknown
|
||||
type NotificationListDTO struct {
|
||||
Notifications []NotificationDTO `json:"notifications"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// NotificationKey collects the identifiers of an Notification
|
||||
type NotificationKey struct {
|
||||
Zone string
|
||||
Type string
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
// RRSetKey generates the RRSetKey for the NotificationKey
|
||||
func (k NotificationKey) RRSetKey() RRSetKey {
|
||||
return RRSetKey{
|
||||
Zone: k.Zone,
|
||||
Type: k.Type,
|
||||
Name: k.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// URI generates the URI for a probe
|
||||
func (k NotificationKey) URI() string {
|
||||
return fmt.Sprintf("%s/%s", k.RRSetKey().NotificationsURI(), k.Email)
|
||||
}
|
||||
|
||||
// Select requests all notifications by RRSetKey and optional query, using pagination and error handling
|
||||
func (s *NotificationsService) Select(k RRSetKey, query string) ([]NotificationDTO, *Response, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
pis := []NotificationDTO{}
|
||||
errcnt := 0
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
reqNotifications, ri, res, err := s.SelectWithOffset(k, query, offset)
|
||||
if err != nil {
|
||||
if res.StatusCode >= 500 {
|
||||
errcnt = errcnt + 1
|
||||
if errcnt < maxerrs {
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return pis, res, err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] ResultInfo: %+v\n", ri)
|
||||
for _, pi := range reqNotifications {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
|
||||
return pis, res, nil
|
||||
}
|
||||
offset = ri.ReturnedCount + ri.Offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset requests list of notifications by RRSetKey, query and offset, also returning list metadata, the actual response, or an error
|
||||
func (s *NotificationsService) SelectWithOffset(k RRSetKey, query string, offset int) ([]NotificationDTO, ResultInfo, *Response, error) {
|
||||
var tld NotificationListDTO
|
||||
|
||||
uri := k.NotificationsQueryURI(query, offset)
|
||||
res, err := s.client.get(uri, &tld)
|
||||
|
||||
log.Printf("DEBUG - ResultInfo: %+v\n", tld.Resultinfo)
|
||||
pis := []NotificationDTO{}
|
||||
for _, pi := range tld.Notifications {
|
||||
pis = append(pis, pi)
|
||||
}
|
||||
return pis, tld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Find requests a notification by NotificationKey,returning the actual response, or an error
|
||||
func (s *NotificationsService) Find(k NotificationKey) (NotificationDTO, *Response, error) {
|
||||
var t NotificationDTO
|
||||
res, err := s.client.get(k.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Create requests creation of an event by RRSetKey, with provided NotificationInfoDTO, returning actual response or an error
|
||||
func (s *NotificationsService) Create(k NotificationKey, n NotificationDTO) (*Response, error) {
|
||||
return s.client.post(k.URI(), n, nil)
|
||||
}
|
||||
|
||||
// Update requests update of an event by NotificationKey, with provided NotificationInfoDTO, returning the actual response or an error
|
||||
func (s *NotificationsService) Update(k NotificationKey, n NotificationDTO) (*Response, error) {
|
||||
return s.client.put(k.URI(), n, nil)
|
||||
}
|
||||
|
||||
// Delete requests deletion of an event by NotificationKey, returning the actual response or an error
|
||||
func (s *NotificationsService) Delete(k NotificationKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ProbeInfoDTO wraps a probe response
|
||||
type ProbeInfoDTO struct {
|
||||
ID string `json:"id"`
|
||||
PoolRecord string `json:"poolRecord"`
|
||||
ProbeType string `json:"type"`
|
||||
Interval string `json:"interval"`
|
||||
Agents []string `json:"agents"`
|
||||
Threshold int `json:"threshold"`
|
||||
Details *ProbeDetailsDTO `json:"details"`
|
||||
}
|
||||
|
||||
// ProbeDetailsLimitDTO wraps a probe
|
||||
type ProbeDetailsLimitDTO struct {
|
||||
Warning int `json:"warning"`
|
||||
Critical int `json:"critical"`
|
||||
Fail int `json:"fail"`
|
||||
}
|
||||
|
||||
// ProbeDetailsDTO wraps the details of a probe
|
||||
type ProbeDetailsDTO struct {
|
||||
data []byte
|
||||
Detail interface{} `json:"detail,omitempty"`
|
||||
typ string
|
||||
}
|
||||
|
||||
// GetData returns the data because I'm working around something.
|
||||
func (s *ProbeDetailsDTO) GetData() []byte {
|
||||
return s.data
|
||||
}
|
||||
|
||||
// Populate does magical things with json unmarshalling to unroll the Probe into
|
||||
// an appropriate datatype. These are helper structures and functions for testing
|
||||
// and direct API use. In the Terraform implementation, we will use Terraforms own
|
||||
// warped schema structure to handle the marshalling and unmarshalling.
|
||||
func (s *ProbeDetailsDTO) Populate(typ string) (err error) {
|
||||
// TODO: actually document
|
||||
switch strings.ToUpper(typ) {
|
||||
case "HTTP":
|
||||
var pp HTTPProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "PING":
|
||||
var pp PingProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "FTP":
|
||||
var pp FTPProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "TCP":
|
||||
var pp TCPProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "SMTP":
|
||||
var pp SMTPProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "SMTP_SEND":
|
||||
var pp SMTPSENDProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
case "DNS":
|
||||
var pp DNSProbeDetailsDTO
|
||||
err = json.Unmarshal(s.data, &pp)
|
||||
s.typ = typ
|
||||
s.Detail = pp
|
||||
return err
|
||||
default:
|
||||
return fmt.Errorf("ERROR - ProbeDetailsDTO.Populate(\"%s\") - Fall through!\n", typ)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON does what it says on the tin
|
||||
func (s *ProbeDetailsDTO) UnmarshalJSON(b []byte) (err error) {
|
||||
s.data = b
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON does what it says on the tin
|
||||
func (s *ProbeDetailsDTO) MarshalJSON() ([]byte, error) {
|
||||
var err error
|
||||
if s.Detail != nil {
|
||||
return json.Marshal(s.Detail)
|
||||
}
|
||||
if len(s.data) != 0 {
|
||||
return s.data, err
|
||||
}
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
// GoString returns a string representation of the ProbeDetailsDTO internal data
|
||||
func (s *ProbeDetailsDTO) GoString() string {
|
||||
return string(s.data)
|
||||
}
|
||||
func (s *ProbeDetailsDTO) String() string {
|
||||
return string(s.data)
|
||||
}
|
||||
|
||||
// Transaction wraps a transaction response
|
||||
type Transaction struct {
|
||||
Method string `json:"method"`
|
||||
URL string `json:"url"`
|
||||
TransmittedData string `json:"transmittedData,omitempty"`
|
||||
FollowRedirects bool `json:"followRedirects,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// HTTPProbeDetailsDTO wraps HTTP probe details
|
||||
type HTTPProbeDetailsDTO struct {
|
||||
Transactions []Transaction `json:"transactions"`
|
||||
TotalLimits *ProbeDetailsLimitDTO `json:"totalLimits,omitempty"`
|
||||
}
|
||||
|
||||
// PingProbeDetailsDTO wraps Ping probe details
|
||||
type PingProbeDetailsDTO struct {
|
||||
Packets int `json:"packets,omitempty"`
|
||||
PacketSize int `json:"packetSize,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// FTPProbeDetailsDTO wraps FTP probe details
|
||||
type FTPProbeDetailsDTO struct {
|
||||
Port int `json:"port,omitempty"`
|
||||
PassiveMode bool `json:"passiveMode,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Path string `json:"path"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// TCPProbeDetailsDTO wraps TCP probe details
|
||||
type TCPProbeDetailsDTO struct {
|
||||
Port int `json:"port,omitempty"`
|
||||
ControlIP string `json:"controlIP,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// SMTPProbeDetailsDTO wraps SMTP probe details
|
||||
type SMTPProbeDetailsDTO struct {
|
||||
Port int `json:"port,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// SMTPSENDProbeDetailsDTO wraps SMTP SEND probe details
|
||||
type SMTPSENDProbeDetailsDTO struct {
|
||||
Port int `json:"port,omitempty"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// DNSProbeDetailsDTO wraps DNS probe details
|
||||
type DNSProbeDetailsDTO struct {
|
||||
Port int `json:"port,omitempty"`
|
||||
TCPOnly bool `json:"tcpOnly,omitempty"`
|
||||
RecordType string `json:"type,omitempty"`
|
||||
OwnerName string `json:"ownerName,omitempty"`
|
||||
Limits map[string]ProbeDetailsLimitDTO `json:"limits"`
|
||||
}
|
||||
|
||||
// ProbeListDTO wraps a list of probes
|
||||
type ProbeListDTO struct {
|
||||
Probes []ProbeInfoDTO `json:"probes"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
// ProbesService manages Probes
|
||||
type ProbesService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// ProbeKey collects the identifiers of a Probe
|
||||
type ProbeKey struct {
|
||||
Zone string
|
||||
Name string
|
||||
ID string
|
||||
}
|
||||
|
||||
// RRSetKey generates the RRSetKey for the ProbeKey
|
||||
func (k ProbeKey) RRSetKey() RRSetKey {
|
||||
return RRSetKey{
|
||||
Zone: k.Zone,
|
||||
Type: "A", // Only A records have probes
|
||||
Name: k.Name,
|
||||
}
|
||||
}
|
||||
|
||||
// URI generates the URI for a probe
|
||||
func (k ProbeKey) URI() string {
|
||||
return fmt.Sprintf("%s/%s", k.RRSetKey().ProbesURI(), k.ID)
|
||||
}
|
||||
|
||||
// Select returns all probes by a RRSetKey, with an optional query
|
||||
func (s *ProbesService) Select(k RRSetKey, query string) ([]ProbeInfoDTO, *Response, error) {
|
||||
var pld ProbeListDTO
|
||||
|
||||
// This API does not support pagination.
|
||||
uri := k.ProbesQueryURI(query)
|
||||
res, err := s.client.get(uri, &pld)
|
||||
|
||||
ps := []ProbeInfoDTO{}
|
||||
if err == nil {
|
||||
for _, t := range pld.Probes {
|
||||
ps = append(ps, t)
|
||||
}
|
||||
}
|
||||
return ps, res, err
|
||||
}
|
||||
|
||||
// Find returns a probe from a ProbeKey
|
||||
func (s *ProbesService) Find(k ProbeKey) (ProbeInfoDTO, *Response, error) {
|
||||
var t ProbeInfoDTO
|
||||
res, err := s.client.get(k.URI(), &t)
|
||||
return t, res, err
|
||||
}
|
||||
|
||||
// Create creates a probe with a RRSetKey using the ProbeInfoDTO dp
|
||||
func (s *ProbesService) Create(k RRSetKey, dp ProbeInfoDTO) (*Response, error) {
|
||||
return s.client.post(k.ProbesURI(), dp, nil)
|
||||
}
|
||||
|
||||
// Update updates a probe given a ProbeKey with the ProbeInfoDTO dp
|
||||
func (s *ProbesService) Update(k ProbeKey, dp ProbeInfoDTO) (*Response, error) {
|
||||
return s.client.put(k.URI(), dp, nil)
|
||||
}
|
||||
|
||||
// Delete deletes a probe by its ProbeKey
|
||||
func (s *ProbesService) Delete(k ProbeKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
# udnssdk - A ultradns SDK for GoLang
|
||||
## about
|
||||
|
||||
This is a golang 'SDK' for UltraDNS that I copapasta'd from weppos/dnsimple.
|
||||
It seemed like an ideal donor since the use case is terraform.
|
||||
|
||||
## How It works:
|
||||
client := udnssdk.NewClient("username","password",udnssdk.DefaultTestBaseURL)
|
||||
|
||||
There is DefaultTestBaseURL and DefaultLiveBaseURL.
|
||||
When you call NewClient, it performs the 'oauth2' authorization step.
|
||||
The refresh token is saved, but not implemented. It should ideally be an error
|
||||
condition triggering a reauth and retry. But since Terraform is the use case, this won't be an issue.
|
||||
|
||||
### RRSet Declaration
|
||||
|
||||
type RRSet struct {
|
||||
OwnerName string `json:"ownerName"`
|
||||
RRType string `json:"rrtype"`
|
||||
TTL int `json:"ttl"`
|
||||
RData []string `json:"rdata"`
|
||||
}
|
||||
|
||||
###GetRRSets(DomainName, RecordName(leave blank for all), RecordType[A|CNAME|ANY])
|
||||
rrsets, resp, err := client.Zones.GetRRSets("domain.com","","ANY")
|
||||
rrsets, resp, err := client.Zones.GetRRSets("domain.com","www","ANY")
|
||||
rrsets, resp, err := client.Zones.GetRRSets("domain.com","","MX")
|
||||
rrsets, resp, err := client.Zones.GetRRSets("domain.com","www","SRV")
|
||||
|
||||
|
||||
|
||||
|
||||
###CreateRRSet(DomainName, RRSet)
|
||||
rr1 := &udnssdk.RRSet{OwnerName: "test", RRType: "A", TTL: 300, RData: []string{"127.0.0.1"}}
|
||||
resp2,err2 := client.Zones.CreateRRSet("ensighten.com",*rr1)
|
||||
|
||||
###UpdateRRSet(DomainName, RRSet)
|
||||
UpdateRRSet requires you to specify the complete RRSet for the update. This implementation does not support PATCHing.
|
||||
|
||||
rr1 := &udnssdk.RRSet{OwnerName: "test", RRType: "A", TTL: 300, RData: []string{"192.168.1.1"}}
|
||||
resp2,err2 := client.Zones.CreateRRSet("domain.com",*rr1)
|
||||
|
||||
###DeleteRRSet(DomainName, RRSet)
|
||||
Delete RRSet only uses the ownerName and RRType values from the RRSet object.
|
||||
|
||||
rr3 := &udnssdk.RRSet{OwnerName: "test", RRType: "A"} // This is permissible.
|
||||
resp3,err3 := client.RRSets.DeleteRRSet("domain.com",*rr3)
|
||||
|
||||
## Example Program
|
||||
|
||||
package main
|
||||
// udnssdk - a golang sdk for the ultradns REST service.
|
||||
// based on weppos/dnsimple
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"udnssdk"
|
||||
)
|
||||
|
||||
|
||||
func main() {
|
||||
client := udnssdk.NewClient("username","password",udnssdk.DefaultTestBaseURL)
|
||||
if client == nil {
|
||||
fmt.Printf("Fail")
|
||||
} else {
|
||||
fmt.Printf("Win\n")
|
||||
rrsets, resp, err := client.RRSets.GetRRSets("domain.com","test","ANY")
|
||||
fmt.Printf("%+v\n",rrsets)
|
||||
fmt.Printf("%+v\n",resp)
|
||||
fmt.Printf("%+v\n",err)
|
||||
fmt.Printf("------------------------\n")
|
||||
fmt.Printf("---- Create RRSet\n")
|
||||
rr1 := &udnssdk.RRSet{OwnerName: "test", RRType: "A", TTL: 300, RData: []string{"127.0.0.1}}
|
||||
resp2,err2 := client.RRSets.CreateRRSet("domain.com",*rr1)
|
||||
fmt.Printf("Resp2: %+v\n", resp2)
|
||||
fmt.Printf("Err2: %+v\n", err2)
|
||||
fmt.Printf("------------------------\n")
|
||||
fmt.Printf("------------------------\n")
|
||||
fmt.Printf("---- Update RRSet\n")
|
||||
fmt.Printf("------------------------\n")
|
||||
rr2 := &udnssdk.RRSet{OwnerName: "test", RRType: "A", TTL: 300, RData: []string{"127.0.0.2"}}
|
||||
resp3, err3 := client.RRSets.UpdateRRSet("domain.com",*rr2)
|
||||
fmt.Printf("Resp3: %+v\n", resp3)
|
||||
fmt.Printf("Err3: %+v\n", err3)
|
||||
fmt.Printf("------------------------\n")
|
||||
|
||||
fmt.Printf("------------------------\n")
|
||||
fmt.Printf("---- Delete RRSet\n")
|
||||
fmt.Printf("------------------------\n")
|
||||
resp4,err4 := client.RRSets.DeleteRRSet("domain.com",*rr2)
|
||||
fmt.Printf("Resp4: %+v\n", resp4)
|
||||
fmt.Printf("Err4: %+v\n", err4)
|
||||
fmt.Printf("------------------------\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#thanks
|
||||
* [weppo's dnsimple go sdk @ github](https://github.com/weppos/go-dnsimple)
|
||||
* [pearkes dnsimple sdk (this one is used by terraform) @ github](https://github.com/pearkes/dnsimple)
|
||||
* [terraform](http://terraform.io)
|
||||
* [UltraDNS's various SDK's](https://github.com/ultradns)
|
|
@ -0,0 +1,377 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RRSetsService provides access to RRSet resources
|
||||
type RRSetsService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Here is the big 'Profile' mess that should get refactored to a more managable place
|
||||
|
||||
// StringProfile wraps a Profile string
|
||||
type StringProfile struct {
|
||||
Profile string `json:"profile,omitempty"`
|
||||
}
|
||||
|
||||
// Metaprofile is a helper struct for extracting a Context from a StringProfile
|
||||
type Metaprofile struct {
|
||||
Context ProfileSchema `json:"@context"`
|
||||
}
|
||||
|
||||
// ProfileSchema are the schema URIs for RRSet Profiles
|
||||
type ProfileSchema string
|
||||
|
||||
const (
|
||||
// DirPoolSchema is the schema URI for a Directional pool profile
|
||||
DirPoolSchema ProfileSchema = "http://schemas.ultradns.com/DirPool.jsonschema"
|
||||
// RDPoolSchema is the schema URI for a Resource Distribution pool profile
|
||||
RDPoolSchema = "http://schemas.ultradns.com/RDPool.jsonschema"
|
||||
// SBPoolSchema is the schema URI for a SiteBacker pool profile
|
||||
SBPoolSchema = "http://schemas.ultradns.com/SBPool.jsonschema"
|
||||
// TCPoolSchema is the schema URI for a Traffic Controller pool profile
|
||||
TCPoolSchema = "http://schemas.ultradns.com/TCPool.jsonschema"
|
||||
)
|
||||
|
||||
// DirPoolProfile wraps a Profile for a Directional Pool
|
||||
type DirPoolProfile struct {
|
||||
Context ProfileSchema `json:"@context"`
|
||||
Description string `json:"description"`
|
||||
ConflictResolve string `json:"conflictResolve,omitempty"`
|
||||
RDataInfo []DPRDataInfo `json:"rdataInfo"`
|
||||
NoResponse DPRDataInfo `json:"noResponse"`
|
||||
}
|
||||
|
||||
// DPRDataInfo wraps the rdataInfo object of a DirPoolProfile response
|
||||
type DPRDataInfo struct {
|
||||
AllNonConfigured bool `json:"allNonConfigured,omitempty"`
|
||||
IPInfo IPInfo `json:"ipInfo,omitempty"`
|
||||
GeoInfo GeoInfo `json:"geoInfo,omitempty"`
|
||||
}
|
||||
|
||||
// IPInfo wraps the ipInfo object of a DPRDataInfo
|
||||
type IPInfo struct {
|
||||
Name string `json:"name"`
|
||||
IsAccountLevel bool `json:"isAccountLevel,omitempty"`
|
||||
Ips []IPAddrDTO `json:"ips"`
|
||||
}
|
||||
|
||||
// GeoInfo wraps the geoInfo object of a DPRDataInfo
|
||||
type GeoInfo struct {
|
||||
Name string `json:"name"`
|
||||
IsAccountLevel bool `json:"isAccountLevel,omitempty"`
|
||||
Codes []string `json:"codes"`
|
||||
}
|
||||
|
||||
// RDPoolProfile wraps a Profile for a Resource Distribution pool
|
||||
type RDPoolProfile struct {
|
||||
Context ProfileSchema `json:"@context"`
|
||||
Order string `json:"order"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
// SBPoolProfile wraps a Profile for a SiteBacker pool
|
||||
type SBPoolProfile struct {
|
||||
Context ProfileSchema `json:"@context"`
|
||||
Description string `json:"description"`
|
||||
RunProbes bool `json:"runProbes,omitempty"`
|
||||
ActOnProbes bool `json:"actOnProbes,omitempty"`
|
||||
Order string `json:"order,omitempty"`
|
||||
MaxActive int `json:"maxActive,omitempty"`
|
||||
MaxServed int `json:"maxServed,omitempty"`
|
||||
RDataInfo []SBRDataInfo `json:"rdataInfo"`
|
||||
BackupRecords []BackupRecord `json:"backupRecords"`
|
||||
}
|
||||
|
||||
// SBRDataInfo wraps the rdataInfo object of a SBPoolProfile
|
||||
type SBRDataInfo struct {
|
||||
State string `json:"state"`
|
||||
RunProbes bool `json:"runProbes,omitempty"`
|
||||
Priority int `json:"priority"`
|
||||
FailoverDelay int `json:"failoverDelay,omitempty"`
|
||||
Threshold int `json:"threshold"`
|
||||
Weight int `json:"weight"`
|
||||
}
|
||||
|
||||
// BackupRecord wraps the backupRecord objects of an SBPoolProfile response
|
||||
type BackupRecord struct {
|
||||
RData string `json:"rdata"`
|
||||
FailoverDelay int `json:"failoverDelay,omitempty"`
|
||||
}
|
||||
|
||||
// TCPoolProfile wraps a Profile for a Traffic Controller pool
|
||||
type TCPoolProfile struct {
|
||||
Context ProfileSchema `json:"@context"`
|
||||
Description string `json:"description"`
|
||||
RunProbes bool `json:"runProbes,omitempty"`
|
||||
ActOnProbes bool `json:"actOnProbes,omitempty"`
|
||||
MaxToLB int `json:"maxToLB,omitempty"`
|
||||
RDataInfo []SBRDataInfo `json:"rdataInfo"`
|
||||
BackupRecord BackupRecord `json:"backupRecord"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON does what it says on the tin
|
||||
func (sp *StringProfile) UnmarshalJSON(b []byte) (err error) {
|
||||
sp.Profile = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON does what it says on the tin
|
||||
func (sp *StringProfile) MarshalJSON() ([]byte, error) {
|
||||
if sp.Profile != "" {
|
||||
return []byte(sp.Profile), nil
|
||||
}
|
||||
return json.Marshal(nil)
|
||||
}
|
||||
|
||||
// Metaprofile converts a StringProfile to a Metaprofile to extract the context
|
||||
func (sp *StringProfile) Metaprofile() (Metaprofile, error) {
|
||||
var mp Metaprofile
|
||||
if sp.Profile == "" {
|
||||
return mp, fmt.Errorf("Empty Profile cannot be converted to a Metaprofile")
|
||||
}
|
||||
err := json.Unmarshal([]byte(sp.Profile), &mp)
|
||||
if err != nil {
|
||||
return mp, fmt.Errorf("Error getting profile type: %+v\n", err)
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// Context extracts the schema context from a StringProfile
|
||||
func (sp *StringProfile) Context() ProfileSchema {
|
||||
mp, err := sp.Metaprofile()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] %+s\n", err)
|
||||
return ""
|
||||
}
|
||||
return mp.Context
|
||||
}
|
||||
|
||||
// GoString returns the StringProfile's Profile.
|
||||
func (sp *StringProfile) GoString() string {
|
||||
return sp.Profile
|
||||
}
|
||||
|
||||
// String returns the StringProfile's Profile.
|
||||
func (sp *StringProfile) String() string {
|
||||
return sp.Profile
|
||||
}
|
||||
|
||||
// GetProfileObject extracts the full Profile by its schema type
|
||||
func (sp *StringProfile) GetProfileObject() interface{} {
|
||||
c := sp.Context()
|
||||
switch c {
|
||||
case DirPoolSchema:
|
||||
var dpp DirPoolProfile
|
||||
err := json.Unmarshal([]byte(sp.Profile), &dpp)
|
||||
if err != nil {
|
||||
log.Printf("Could not Unmarshal the DirPoolProfile.\n")
|
||||
return nil
|
||||
}
|
||||
return dpp
|
||||
case RDPoolSchema:
|
||||
var rdp RDPoolProfile
|
||||
err := json.Unmarshal([]byte(sp.Profile), &rdp)
|
||||
if err != nil {
|
||||
log.Printf("Could not Unmarshal the RDPoolProfile.\n")
|
||||
return nil
|
||||
}
|
||||
return rdp
|
||||
case SBPoolSchema:
|
||||
var sbp SBPoolProfile
|
||||
err := json.Unmarshal([]byte(sp.Profile), &sbp)
|
||||
if err != nil {
|
||||
log.Printf("Could not Unmarshal the SBPoolProfile.\n")
|
||||
return nil
|
||||
}
|
||||
return sbp
|
||||
case TCPoolSchema:
|
||||
var tcp TCPoolProfile
|
||||
err := json.Unmarshal([]byte(sp.Profile), &tcp)
|
||||
if err != nil {
|
||||
log.Printf("Could not Unmarshal the TCPoolProfile.\n")
|
||||
return nil
|
||||
}
|
||||
return tcp
|
||||
default:
|
||||
log.Printf("ERROR - Fall through on GetProfileObject - %s.\n", c)
|
||||
return fmt.Errorf("Fallthrough on GetProfileObject type %s\n", c)
|
||||
}
|
||||
}
|
||||
|
||||
// RRSet wraps an RRSet resource
|
||||
type RRSet struct {
|
||||
OwnerName string `json:"ownerName"`
|
||||
RRType string `json:"rrtype"`
|
||||
TTL int `json:"ttl"`
|
||||
RData []string `json:"rdata"`
|
||||
Profile *StringProfile `json:"profile,omitempty"`
|
||||
}
|
||||
|
||||
// RRSetListDTO wraps a list of RRSet resources
|
||||
type RRSetListDTO struct {
|
||||
ZoneName string `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
|
||||
Type string
|
||||
Name string
|
||||
}
|
||||
|
||||
// URI generates the URI for an RRSet
|
||||
func (k RRSetKey) URI() string {
|
||||
uri := fmt.Sprintf("zones/%s/rrsets", k.Zone)
|
||||
if k.Type != "" {
|
||||
uri += fmt.Sprintf("/%v", k.Type)
|
||||
if k.Name != "" {
|
||||
uri += fmt.Sprintf("/%v", k.Name)
|
||||
}
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// QueryURI generates the query URI for an RRSet and offset
|
||||
func (k RRSetKey) QueryURI(offset int) string {
|
||||
// TODO: find a more appropriate place to set "" to "ANY"
|
||||
if k.Type == "" {
|
||||
k.Type = "ANY"
|
||||
}
|
||||
return fmt.Sprintf("%s?offset=%d", k.URI(), offset)
|
||||
}
|
||||
|
||||
// AlertsURI generates the URI for an RRSet
|
||||
func (k RRSetKey) AlertsURI() string {
|
||||
return fmt.Sprintf("%s/alerts", k.URI())
|
||||
}
|
||||
|
||||
// AlertsQueryURI generates the alerts query URI for an RRSet with query
|
||||
func (k RRSetKey) AlertsQueryURI(offset int) string {
|
||||
uri := k.AlertsURI()
|
||||
if offset != 0 {
|
||||
uri = fmt.Sprintf("%s?offset=%d", uri, offset)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// EventsURI generates the URI for an RRSet
|
||||
func (k RRSetKey) EventsURI() string {
|
||||
return fmt.Sprintf("%s/events", k.URI())
|
||||
}
|
||||
|
||||
// EventsQueryURI generates the events query URI for an RRSet with query
|
||||
func (k RRSetKey) EventsQueryURI(query string, offset int) string {
|
||||
uri := k.EventsURI()
|
||||
if query != "" {
|
||||
return fmt.Sprintf("%s?sort=NAME&query=%s&offset=%d", uri, query, offset)
|
||||
}
|
||||
if offset != 0 {
|
||||
return fmt.Sprintf("%s?offset=%d", uri, offset)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// NotificationsURI generates the notifications URI for an RRSet
|
||||
func (k RRSetKey) NotificationsURI() string {
|
||||
return fmt.Sprintf("%s/notifications", k.URI())
|
||||
}
|
||||
|
||||
// NotificationsQueryURI generates the notifications query URI for an RRSet with query
|
||||
func (k RRSetKey) NotificationsQueryURI(query string, offset int) string {
|
||||
uri := k.NotificationsURI()
|
||||
if query != "" {
|
||||
uri = fmt.Sprintf("%s?sort=NAME&query=%s&offset=%d", uri, query, offset)
|
||||
} else {
|
||||
uri = fmt.Sprintf("%s?offset=%d", uri, offset)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// ProbesURI generates the probes URI for an RRSet
|
||||
func (k RRSetKey) ProbesURI() string {
|
||||
return fmt.Sprintf("%s/probes", k.URI())
|
||||
}
|
||||
|
||||
// ProbesQueryURI generates the probes query URI for an RRSet with query
|
||||
func (k RRSetKey) ProbesQueryURI(query string) string {
|
||||
uri := k.ProbesURI()
|
||||
if query != "" {
|
||||
uri = fmt.Sprintf("%s?sort=NAME&query=%s", uri, query)
|
||||
}
|
||||
return uri
|
||||
}
|
||||
|
||||
// Select will list the zone rrsets, paginating through all available results
|
||||
func (s *RRSetsService) Select(k RRSetKey) ([]RRSet, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
rrsets := []RRSet{}
|
||||
errcnt := 0
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
reqRrsets, ri, res, err := s.SelectWithOffset(k, offset)
|
||||
if err != nil {
|
||||
if res.StatusCode >= 500 {
|
||||
errcnt = errcnt + 1
|
||||
if errcnt < maxerrs {
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return rrsets, err
|
||||
}
|
||||
|
||||
log.Printf("ResultInfo: %+v\n", ri)
|
||||
for _, rrset := range reqRrsets {
|
||||
rrsets = append(rrsets, rrset)
|
||||
}
|
||||
if ri.ReturnedCount+ri.Offset >= ri.TotalCount {
|
||||
return rrsets, nil
|
||||
}
|
||||
offset = ri.ReturnedCount + ri.Offset
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset requests zone rrsets by RRSetKey & optional offset
|
||||
func (s *RRSetsService) SelectWithOffset(k RRSetKey, offset int) ([]RRSet, ResultInfo, *Response, error) {
|
||||
var rrsld RRSetListDTO
|
||||
|
||||
uri := k.QueryURI(offset)
|
||||
res, err := s.client.get(uri, &rrsld)
|
||||
|
||||
rrsets := []RRSet{}
|
||||
for _, rrset := range rrsld.Rrsets {
|
||||
rrsets = append(rrsets, rrset)
|
||||
}
|
||||
return rrsets, rrsld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Create creates an rrset with val
|
||||
func (s *RRSetsService) Create(k RRSetKey, rrset RRSet) (*Response, error) {
|
||||
var ignored interface{}
|
||||
return s.client.post(k.URI(), rrset, &ignored)
|
||||
}
|
||||
|
||||
// Update updates a RRSet with the provided val
|
||||
func (s *RRSetsService) Update(k RRSetKey, val RRSet) (*Response, error) {
|
||||
var ignored interface{}
|
||||
return s.client.put(k.URI(), val, &ignored)
|
||||
}
|
||||
|
||||
// Delete deletes an RRSet
|
||||
func (s *RRSetsService) Delete(k RRSetKey) (*Response, error) {
|
||||
return s.client.delete(k.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package udnssdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TasksService provides access to the tasks resources
|
||||
type TasksService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// Task wraps a task response
|
||||
type Task struct {
|
||||
TaskID string `json:"taskId"`
|
||||
TaskStatusCode string `json:"taskStatusCode"`
|
||||
Message string `json:"message"`
|
||||
ResultURI string `json:"resultUri"`
|
||||
}
|
||||
|
||||
// TaskListDTO wraps a list of Task resources, from an HTTP response
|
||||
type TaskListDTO struct {
|
||||
Tasks []Task `json:"tasks"`
|
||||
Queryinfo QueryInfo `json:"queryInfo"`
|
||||
Resultinfo ResultInfo `json:"resultInfo"`
|
||||
}
|
||||
|
||||
type taskWrapper struct {
|
||||
Task Task `json:"task"`
|
||||
}
|
||||
|
||||
// TaskID represents the string identifier of a task
|
||||
type TaskID string
|
||||
|
||||
// ResultURI generates URI for the task result
|
||||
func (t TaskID) ResultURI() string {
|
||||
return fmt.Sprintf("%s/result", t.URI())
|
||||
}
|
||||
|
||||
// URI generates the URI for a task
|
||||
func (t TaskID) URI() string {
|
||||
return fmt.Sprintf("tasks/%s", t)
|
||||
}
|
||||
|
||||
// TasksQueryURI generates the query URI for the tasks collection given a query and offset
|
||||
func TasksQueryURI(query string, offset int) string {
|
||||
if query != "" {
|
||||
return fmt.Sprintf("tasks?sort=NAME&query=%s&offset=%d", query, offset)
|
||||
}
|
||||
return fmt.Sprintf("tasks?offset=%d", offset)
|
||||
}
|
||||
|
||||
// Select requests all tasks, with pagination
|
||||
func (s *TasksService) Select(query string) ([]Task, error) {
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
maxerrs := 5
|
||||
waittime := 5 * time.Second
|
||||
|
||||
// init accumulators
|
||||
dtos := []Task{}
|
||||
offset := 0
|
||||
errcnt := 0
|
||||
|
||||
for {
|
||||
reqDtos, ri, res, err := s.SelectWithOffset(query, offset)
|
||||
if err != nil {
|
||||
if 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
|
||||
}
|
||||
}
|
||||
|
||||
// SelectWithOffset request tasks by query & offset, list them also returning list metadata, the actual response, or an error
|
||||
func (s *TasksService) SelectWithOffset(query string, offset int) ([]Task, ResultInfo, *Response, error) {
|
||||
var tld TaskListDTO
|
||||
|
||||
uri := TasksQueryURI(query, offset)
|
||||
res, err := s.client.get(uri, &tld)
|
||||
|
||||
ts := []Task{}
|
||||
for _, t := range tld.Tasks {
|
||||
ts = append(ts, t)
|
||||
}
|
||||
return ts, tld.Resultinfo, res, err
|
||||
}
|
||||
|
||||
// Find Get the status of a task.
|
||||
func (s *TasksService) Find(t TaskID) (Task, *Response, error) {
|
||||
var tv Task
|
||||
res, err := s.client.get(t.URI(), &tv)
|
||||
return tv, res, err
|
||||
}
|
||||
|
||||
// FindResult requests
|
||||
func (s *TasksService) FindResult(t TaskID) (*Response, error) {
|
||||
return s.client.GetResultByURI(t.ResultURI())
|
||||
}
|
||||
|
||||
// FindResultByTask requests a task by the provided task's result uri
|
||||
func (s *TasksService) FindResultByTask(t Task) (*Response, error) {
|
||||
return s.client.GetResultByURI(t.ResultURI)
|
||||
}
|
||||
|
||||
// Delete requests deletions
|
||||
func (s *TasksService) Delete(t TaskID) (*Response, error) {
|
||||
return s.client.delete(t.URI(), nil)
|
||||
}
|
|
@ -0,0 +1,377 @@
|
|||
package udnssdk
|
||||
|
||||
// udnssdk - a golang sdk for the ultradns REST service.
|
||||
// 2015-07-03 - jmasseo@gmail.com
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
libraryVersion = "0.1"
|
||||
// DefaultTestBaseURL returns the URL for UltraDNS's test restapi endpoint
|
||||
DefaultTestBaseURL = "https://test-restapi.ultradns.com/"
|
||||
// DefaultLiveBaseURL returns the URL for UltraDNS's production restapi endpoint
|
||||
DefaultLiveBaseURL = "https://restapi.ultradns.com/"
|
||||
|
||||
userAgent = "udnssdk-go/" + libraryVersion
|
||||
|
||||
apiVersion = "v1"
|
||||
)
|
||||
|
||||
// QueryInfo wraps a query request
|
||||
type QueryInfo struct {
|
||||
Q string `json:"q"`
|
||||
Sort string `json:"sort"`
|
||||
Reverse bool `json:"reverse"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
// ResultInfo wraps the list metadata for an index response
|
||||
type ResultInfo struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
Offset int `json:"offset"`
|
||||
ReturnedCount int `json:"returnedCount"`
|
||||
}
|
||||
|
||||
// Client wraps our general-purpose Service Client
|
||||
type Client struct {
|
||||
// This is our client structure.
|
||||
HTTPClient *http.Client
|
||||
|
||||
// UltraDNS makes a call to an authorization API using your username and
|
||||
// password, returning an 'Access Token' and a 'Refresh Token'.
|
||||
// Our use case does not require the refresh token, but we should implement
|
||||
// for completeness.
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
Username string
|
||||
Password string
|
||||
BaseURL string
|
||||
UserAgent string
|
||||
|
||||
// Accounts API
|
||||
Accounts *AccountsService
|
||||
// Probe Alerts API
|
||||
Alerts *AlertsService
|
||||
// Directional Pools API
|
||||
DirectionalPools *DirectionalPoolsService
|
||||
// Events API
|
||||
Events *EventsService
|
||||
// Notifications API
|
||||
Notifications *NotificationsService
|
||||
// Probes API
|
||||
Probes *ProbesService
|
||||
// Resource Record Sets API
|
||||
RRSets *RRSetsService
|
||||
// Tasks API
|
||||
Tasks *TasksService
|
||||
}
|
||||
|
||||
// NewClient returns a new ultradns API client.
|
||||
func NewClient(username, password, BaseURL string) (*Client, error) {
|
||||
accesstoken, refreshtoken, err := GetAuthTokens(username, password, BaseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &Client{
|
||||
AccessToken: accesstoken,
|
||||
RefreshToken: refreshtoken,
|
||||
Username: username,
|
||||
Password: password,
|
||||
HTTPClient: &http.Client{},
|
||||
BaseURL: BaseURL,
|
||||
UserAgent: userAgent,
|
||||
}
|
||||
c.Accounts = &AccountsService{client: c}
|
||||
c.Alerts = &AlertsService{client: c}
|
||||
c.DirectionalPools = &DirectionalPoolsService{client: c}
|
||||
c.Events = &EventsService{client: c}
|
||||
c.Notifications = &NotificationsService{client: c}
|
||||
c.Probes = &ProbesService{client: c}
|
||||
c.RRSets = &RRSetsService{client: c}
|
||||
c.Tasks = &TasksService{client: c}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// newStubClient returns a new ultradns API client.
|
||||
func newStubClient(username, password, BaseURL, accesstoken, refreshtoken string) (*Client, error) {
|
||||
c := &Client{
|
||||
AccessToken: accesstoken,
|
||||
RefreshToken: refreshtoken,
|
||||
Username: username,
|
||||
Password: password,
|
||||
HTTPClient: &http.Client{},
|
||||
BaseURL: BaseURL,
|
||||
UserAgent: userAgent,
|
||||
}
|
||||
c.Accounts = &AccountsService{client: c}
|
||||
c.Alerts = &AlertsService{client: c}
|
||||
c.DirectionalPools = &DirectionalPoolsService{client: c}
|
||||
c.Events = &EventsService{client: c}
|
||||
c.Notifications = &NotificationsService{client: c}
|
||||
c.Probes = &ProbesService{client: c}
|
||||
c.RRSets = &RRSetsService{client: c}
|
||||
c.Tasks = &TasksService{client: c}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewAuthRequest creates an Authorization request to get an access and refresh token.
|
||||
//
|
||||
// {
|
||||
// "tokenType":"Bearer",
|
||||
// "refreshToken":"48472efcdce044c8850ee6a395c74a7872932c7112",
|
||||
// "accessToken":"b91d037c75934fc89a9f43fe4a",
|
||||
// "expiresIn":"3600",
|
||||
// "expires_in":"3600"
|
||||
// }
|
||||
|
||||
// AuthResponse wraps the response to an auth request
|
||||
type AuthResponse struct {
|
||||
TokenType string `json:"tokenType"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
ExpiresIn string `json:"expiresIn"`
|
||||
}
|
||||
|
||||
// GetAuthTokens requests by username, password & base URL, returns the access-token & refresh-token, or a possible error
|
||||
func GetAuthTokens(username, password, BaseURL string) (string, string, error) {
|
||||
res, err := http.PostForm(fmt.Sprintf("%s/%s/authorization/token", BaseURL, apiVersion), url.Values{"grant_type": {"password"}, "username": {username}, "password": {password}})
|
||||
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
//response := &Response{Response: res}
|
||||
defer res.Body.Close()
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
err = CheckAuthResponse(res, body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var authr AuthResponse
|
||||
err = json.Unmarshal(body, &authr)
|
||||
if err != nil {
|
||||
return string(body), "JSON Decode Error", err
|
||||
}
|
||||
return authr.AccessToken, authr.RefreshToken, err
|
||||
}
|
||||
|
||||
// NewRequest creates an API request.
|
||||
// The path is expected to be a relative path and will be resolved
|
||||
// according to the BaseURL of the Client. Paths should always be specified without a preceding slash.
|
||||
func (c *Client) NewRequest(method, path string, payload interface{}) (*http.Request, error) {
|
||||
url := c.BaseURL + fmt.Sprintf("%s/%s", apiVersion, path)
|
||||
|
||||
body := new(bytes.Buffer)
|
||||
if payload != nil {
|
||||
err := json.NewEncoder(body).Encode(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Add("Accept", "application/json")
|
||||
req.Header.Add("User-Agent", c.UserAgent)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.AccessToken))
|
||||
req.Header.Add("Token", fmt.Sprintf("Bearer %s", c.AccessToken))
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) get(path string, v interface{}) (*Response, error) {
|
||||
return c.Do("GET", path, nil, v)
|
||||
}
|
||||
|
||||
func (c *Client) post(path string, payload, v interface{}) (*Response, error) {
|
||||
return c.Do("POST", path, payload, v)
|
||||
}
|
||||
|
||||
func (c *Client) put(path string, payload, v interface{}) (*Response, error) {
|
||||
return c.Do("PUT", path, payload, v)
|
||||
}
|
||||
|
||||
func (c *Client) delete(path string, payload interface{}) (*Response, error) {
|
||||
return c.Do("DELETE", path, payload, nil)
|
||||
}
|
||||
|
||||
// Do sends an API request and returns the API response.
|
||||
// The API response is JSON decoded and stored in the value pointed by v,
|
||||
// or returned as an error if an API error has occurred.
|
||||
// If v implements the io.Writer interface, the raw response body will be written to v,
|
||||
// without attempting to decode it.
|
||||
func (c *Client) Do(method, path string, payload, v interface{}) (*Response, error) {
|
||||
req, err := c.NewRequest(method, path, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("[DEBUG] HTTP Request: %+v\n", req)
|
||||
res, err := c.HTTPClient.Do(req)
|
||||
log.Printf("[DEBUG] HTTP Response: %+v\n", res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
origresponse := &Response{Response: res}
|
||||
|
||||
var nres *http.Response
|
||||
nres = res
|
||||
if res.StatusCode == 202 {
|
||||
// This is a deferred task.
|
||||
tid := TaskID(res.Header.Get("X-Task-Id"))
|
||||
log.Printf("[DEBUG] Received Async Task %+v.. will retry...\n", tid)
|
||||
// TODO: Sane Configuration for timeouts / retries
|
||||
timeout := 5
|
||||
waittime := 5 * time.Second
|
||||
i := 0
|
||||
breakmeout := false
|
||||
for i < timeout || breakmeout {
|
||||
myt, statusres, err := c.Tasks.Find(tid)
|
||||
if err != nil {
|
||||
return origresponse, err
|
||||
}
|
||||
log.Printf("[DEBUG] Task ID: %+v Retry: %d Status Code: %s\n", tid, i, myt.TaskStatusCode)
|
||||
switch myt.TaskStatusCode {
|
||||
case "COMPLETE":
|
||||
// Yay
|
||||
tres, err := c.Tasks.FindResultByTask(myt)
|
||||
if err != nil {
|
||||
return origresponse, err
|
||||
}
|
||||
nres = tres.Response
|
||||
breakmeout = true
|
||||
case "PENDING", "IN_PROCESS":
|
||||
i = i + 1
|
||||
time.Sleep(waittime)
|
||||
continue
|
||||
case "ERROR":
|
||||
return statusres, err
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
response := &Response{Response: nres}
|
||||
|
||||
err = CheckResponse(nres)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
|
||||
if v != nil {
|
||||
if w, ok := v.(io.Writer); ok {
|
||||
io.Copy(w, res.Body)
|
||||
} else {
|
||||
err = json.NewDecoder(res.Body).Decode(v)
|
||||
}
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
// A Response represents an API response.
|
||||
type Response struct {
|
||||
*http.Response
|
||||
}
|
||||
|
||||
// ErrorResponse represents an error caused by an API request.
|
||||
// Example:
|
||||
// {"errorCode":60001,"errorMessage":"invalid_grant:Invalid username & password combination.","error":"invalid_grant","error_description":"60001: invalid_grant:Invalid username & password combination."}
|
||||
type ErrorResponse struct {
|
||||
Response *http.Response // HTTP response that caused this error
|
||||
ErrorCode int `json:"errorCode"` // error code
|
||||
ErrorMessage string `json:"errorMessage"` // human-readable message
|
||||
ErrorStr string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
// ErrorResponseList wraps an HTTP response that has a list of errors
|
||||
type ErrorResponseList struct {
|
||||
Response *http.Response // HTTP response that caused this error
|
||||
Responses []ErrorResponse
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (r ErrorResponse) Error() string {
|
||||
return fmt.Sprintf("%v %v: %d %d %v",
|
||||
r.Response.Request.Method, r.Response.Request.URL,
|
||||
r.Response.StatusCode, r.ErrorCode, r.ErrorMessage)
|
||||
}
|
||||
|
||||
func (r ErrorResponseList) Error() string {
|
||||
return fmt.Sprintf("%v %v: %d %d %v",
|
||||
r.Response.Request.Method, r.Response.Request.URL,
|
||||
r.Response.StatusCode, r.Responses[0].ErrorCode, r.Responses[0].ErrorMessage)
|
||||
}
|
||||
|
||||
// CheckAuthResponse checks the API response for errors, and returns them if so
|
||||
func CheckAuthResponse(r *http.Response, body []byte) error {
|
||||
if code := r.StatusCode; 200 <= code && code <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attempt marshaling to ErrorResponse
|
||||
var er ErrorResponse
|
||||
err := json.Unmarshal(body, &er)
|
||||
if err == nil {
|
||||
er.Response = r
|
||||
return er
|
||||
}
|
||||
|
||||
// Attempt marshaling to ErrorResponseList
|
||||
var ers []ErrorResponse
|
||||
err = json.Unmarshal(body, &ers)
|
||||
if err == nil {
|
||||
return &ErrorResponseList{Response: r, Responses: ers}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Response had non-successful status: %d, but could not extract error from body: %+v", r.StatusCode, body)
|
||||
}
|
||||
|
||||
// CheckResponse checks the API response for errors, and returns them if present.
|
||||
// A response is considered an error if the status code is different than 2xx. Specific requests
|
||||
// may have additional requirements, but this is sufficient in most of the cases.
|
||||
func CheckResponse(r *http.Response) error {
|
||||
if code := r.StatusCode; 200 <= code && code <= 299 {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Attempt marshaling to ErrorResponse
|
||||
var er ErrorResponse
|
||||
err = json.Unmarshal(body, &er)
|
||||
if err == nil {
|
||||
er.Response = r
|
||||
return er
|
||||
}
|
||||
|
||||
// Attempt marshaling to ErrorResponseList
|
||||
var ers []ErrorResponse
|
||||
err = json.Unmarshal(body, &ers)
|
||||
if err == nil {
|
||||
return &ErrorResponseList{Response: r, Responses: ers}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Response had non-successful status: %d, but could not extract error from body: %+v", r.StatusCode, body)
|
||||
}
|
|
@ -33,6 +33,7 @@ body.layout-rundeck,
|
|||
body.layout-statuscake,
|
||||
body.layout-template,
|
||||
body.layout-tls,
|
||||
body.layout-ultradns,
|
||||
body.layout-vcd,
|
||||
body.layout-vsphere,
|
||||
body.layout-docs,
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: "ultradns"
|
||||
page_title: "Provider: UltraDNS"
|
||||
sidebar_current: "docs-ultradns-index"
|
||||
description: |-
|
||||
The UltraDNS provider is used to interact with the resources supported by UltraDNS. The provider needs to be configured with the proper credentials before it can be used.
|
||||
---
|
||||
|
||||
# UltraDNS Provider
|
||||
|
||||
The UltraDNS provider is used to interact with the
|
||||
resources supported by UltraDNS. The provider needs to be configured
|
||||
with the proper credentials before it can be used.
|
||||
|
||||
Use the navigation to the left to read about the available resources.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
# Configure the UltraDNS provider
|
||||
provider "ultradns" {
|
||||
username = "${var.ultradns_username}"
|
||||
password = "${var.ultradns_password}"
|
||||
baseurl = "https://test-restapi.ultradns.com/"
|
||||
}
|
||||
|
||||
# Create a record
|
||||
resource "ultradns_record" "www" {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `username` - (Required) The UltraDNS username. It must be provided, but it can also be sourced from the `ULTRADNS_USERNAME` environment variable.
|
||||
* `password` - (Required) The password associated with the username. It must be provided, but it can also be sourced from the `ULTRADNS_PASSWORD` environment variable.
|
||||
* `baseurl` - (Required) The base url for the UltraDNS REST API, but it can also be sourced from the `ULTRADNS_BASEURL` environment variable.
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
layout: "ultradns"
|
||||
page_title: "UltraDNS: ultradns_record"
|
||||
sidebar_current: "docs-ultradns-resource-record"
|
||||
description: |-
|
||||
Provides a UltraDNS record resource.
|
||||
---
|
||||
|
||||
# ultradns\_record
|
||||
|
||||
Provides a UltraDNS record resource.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
# Add a record to the domain
|
||||
resource "ultradns_record" "foobar" {
|
||||
zone = "${var.ultradns_domain}"
|
||||
name = "terraform"
|
||||
rdata = [ "192.168.0.11" ]
|
||||
type = "A"
|
||||
ttl = 3600
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
See [related part of UltraDNS Docs](https://restapi.ultradns.com/v1/docs#post-rrset) for details about valid values.
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `zone` - (Required) The domain to add the record to
|
||||
* `name` - (Required) The name of the record
|
||||
* `rdata` - (Required) An array containing the values of the record
|
||||
* `type` - (Required) The type of the record
|
||||
* `ttl` - (Optional) The TTL of the record
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - The record ID
|
||||
* `name` - The name of the record
|
||||
* `rdata` - An array containing the values of the record
|
||||
* `type` - The type of the record
|
||||
* `ttl` - The TTL of the record
|
||||
* `zone` - The domain of the record
|
||||
* `hostname` - The FQDN of the record
|
|
@ -241,6 +241,10 @@
|
|||
<a href="/docs/providers/tls/index.html">TLS</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-ultradns") %>>
|
||||
<a href="/docs/providers/ultradns/index.html">UltraDNS</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-providers-vcd") %>>
|
||||
<a href="/docs/providers/vcd/index.html">VMware vCloud Director</a>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<% wrap_layout :inner do %>
|
||||
<% content_for :sidebar do %>
|
||||
<div class="docs-sidebar hidden-print affix-top" role="complementary">
|
||||
<ul class="nav docs-sidenav">
|
||||
<li<%= sidebar_current("docs-home") %>>
|
||||
<a href="/docs/providers/index.html">« Documentation Home</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-ultradns-index") %>>
|
||||
<a href="/docs/providers/ultradns/index.html">UltraDNS Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-ultradns-resource/) %>>
|
||||
<a href="#">Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-ultradns-resource-record") %>>
|
||||
<a href="/docs/providers/ultradns/r/record.html">ultradns_record</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= yield %>
|
||||
<% end %>
|
Loading…
Reference in New Issue