UltraDNS Provider
This commit is contained in:
parent
85b4b5813f
commit
1b4991163f
|
@ -154,6 +154,10 @@
|
||||||
"ImportPath": "github.com/DreamItGetIT/statuscake",
|
"ImportPath": "github.com/DreamItGetIT/statuscake",
|
||||||
"Rev": "8cbe86575f00210a6df2c19cb2f59b00cd181de3"
|
"Rev": "8cbe86575f00210a6df2c19cb2f59b00cd181de3"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/Ensighten/udnssdk",
|
||||||
|
"Rev": "0290933f5e8afd933f2823fce32bf2847e6ea603"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/apparentlymart/go-cidr/cidr",
|
"ImportPath": "github.com/apparentlymart/go-cidr/cidr",
|
||||||
"Rev": "a3ebdb999b831ecb6ab8a226e31b07b2b9061c47"
|
"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-statuscake,
|
||||||
body.layout-template,
|
body.layout-template,
|
||||||
body.layout-tls,
|
body.layout-tls,
|
||||||
|
body.layout-ultradns,
|
||||||
body.layout-vcd,
|
body.layout-vcd,
|
||||||
body.layout-vsphere,
|
body.layout-vsphere,
|
||||||
body.layout-docs,
|
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>
|
<a href="/docs/providers/tls/index.html">TLS</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-providers-ultradns") %>>
|
||||||
|
<a href="/docs/providers/ultradns/index.html">UltraDNS</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-providers-vcd") %>>
|
<li<%= sidebar_current("docs-providers-vcd") %>>
|
||||||
<a href="/docs/providers/vcd/index.html">VMware vCloud Director</a>
|
<a href="/docs/providers/vcd/index.html">VMware vCloud Director</a>
|
||||||
</li>
|
</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