Ns1 provider (#10782)

* vendor: update gopkg.in/ns1/ns1-go.v2

* provider/ns1: Port the ns1 provider to Terraform core

* docs/ns1: Document the ns1 provider

* ns1: rename remaining nsone -> ns1 (#10805)

* Ns1 provider (#11300)

* provider/ns1: Flesh out support for meta structs.

Following the structure outlined by @pashap.

Using reflection to reduce copy/paste.

Putting metas inside single-item lists.  This is clunky, but I couldn't
figure out how else to have a nested struct.  Maybe the Terraform people
know a better way?

Inside the meta struct, all fields are always written to the state; I
can't figure out how to omit fields that aren't used.  This is not just
verbose, it actually causes issues because you can't have both "up" and
"up_feed" set).

Also some minor other changes:
- Add "terraform" import support to records and zones.
- Create helper class StringEnum.

* provider/ns1: Make fmt

* provider/ns1: Remove stubbed out RecordRead (used for testing metadata change).

* provider/ns1: Need to get interface that m contains from Ptr Value with Elem()

* provider/ns1: Use empty string to indicate no feed given.

* provider/ns1: Remove old record.regions fields.

* provider/ns1: Removes redundant testAccCheckRecordState

* provider/ns1: Moves account permissions logic to permissions.go

* provider/ns1: Adds tests for team resource.

* provider/ns1: Move remaining permissions logic to permissions.go

* ns1/provider: Adds datasource.config

* provider/ns1: Small clean up of datafeed resource tests

* provider/ns1: removes testAccCheckZoneState in favor of explicit name check

* provider/ns1: More renaming of nsone -> ns1

* provider/ns1: Comment out metadata for the moment.

* Ns1 provider (#11347)

* Fix the removal of empty containers from a flatmap

Removal of empty nested containers from a flatmap would sometimes fail a
sanity check when removed in the wrong order. This would only fail
sometimes due to map iteration. There was also an off-by-one error in
the prefix check which could match the incorrect keys.

* provider/ns1: Adds ns1 go client through govendor.

* provider/ns1: Removes unused debug line

* docs/ns1: Adds docs around apikey/datasource/datafeed/team/user/record.

* provider/ns1: Gets go vet green
This commit is contained in:
Paul Stack 2017-01-23 21:41:07 +00:00 committed by GitHub
parent 08dcd58923
commit 987b910828
83 changed files with 7615 additions and 0 deletions

View File

@ -0,0 +1,12 @@
package main
import (
"github.com/hashicorp/terraform/builtin/providers/ns1"
"github.com/hashicorp/terraform/plugin"
)
func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: ns1.Provider,
})
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,218 @@
package ns1
import (
"fmt"
"reflect"
"github.com/hashicorp/terraform/helper/schema"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
type TfSchemaBuilder func(*schema.Schema)
func mtSimple(t schema.ValueType) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = t
}
}
func mtStringEnum(se *StringEnum) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = schema.TypeString
s.ValidateFunc = func(v interface{}, k string) ([]string, []error) {
_, err := se.Check(v.(string))
if err != nil {
return nil, []error{err}
}
return nil, nil
}
}
}
var mtInt TfSchemaBuilder = mtSimple(schema.TypeInt)
var mtBool TfSchemaBuilder = mtSimple(schema.TypeBool)
var mtString TfSchemaBuilder = mtSimple(schema.TypeString)
var mtFloat64 TfSchemaBuilder = mtSimple(schema.TypeFloat)
func mtList(elementSchemaBuilder TfSchemaBuilder) TfSchemaBuilder {
return func(s *schema.Schema) {
s.Type = schema.TypeList
elementSchema := &schema.Schema{}
elementSchemaBuilder(elementSchema)
s.Elem = elementSchema
}
}
var mtStringList TfSchemaBuilder = mtList(mtString)
type MetaFieldSpec struct {
NameInDynamic string
NameInStruct string
SchemaBuilder TfSchemaBuilder
}
type MetaField struct {
MetaFieldSpec
NameInDynamicForFeed string
StructIndex int
StructGoType reflect.Type
}
var georegionEnum *StringEnum = NewStringEnum([]string{
"US-WEST",
"US-EAST",
"US-CENTRAL",
"EUROPE",
"AFRICA",
"ASIAPAC",
"SOUTH-AMERICA",
})
func makeMetaFields() []MetaField {
var specs []MetaFieldSpec = []MetaFieldSpec{
{"up", "Up", mtBool},
{"connections", "Connections", mtInt},
{"requests", "Requests", mtInt},
{"loadavg", "LoadAvg", mtFloat64},
{"pulsar", "Pulsar", mtInt},
{"latitude", "Latitude", mtFloat64},
{"longitude", "Longitude", mtFloat64},
{"georegion", "Georegion", mtList(mtStringEnum(georegionEnum))},
{"country", "Country", mtStringList},
{"us_state", "USState", mtStringList},
{"ca_province", "CAProvince", mtStringList},
{"note", "Note", mtString},
{"ip_prefixes", "IPPrefixes", mtStringList},
{"asn", "ASN", mtList(mtInt)},
{"priority", "Priority", mtInt},
{"weight", "Weight", mtFloat64},
{"low_watermark", "LowWatermark", mtInt},
{"high_watermark", "HighWatermark", mtInt},
}
// Figure out the field indexes (in data.Meta) for all the fields.
// This way we can later lookup by index, which should be faster than by name.
rt := reflect.TypeOf(data.Meta{})
fields := make([]MetaField, len(specs))
for i, spec := range specs {
rf, present := rt.FieldByName(spec.NameInStruct)
if !present {
panic(fmt.Sprintf("Field %q not present", spec.NameInStruct))
}
if len(rf.Index) != 1 {
panic(fmt.Sprintf("Expecting a single index, got %#v", rf.Index))
}
index := rf.Index[0]
fields[i] = MetaField{
MetaFieldSpec: spec,
StructIndex: index,
NameInDynamicForFeed: spec.NameInDynamic + "_feed",
StructGoType: rf.Type,
}
}
return fields
}
var metaFields []MetaField = makeMetaFields()
func makeMetaSchema() *schema.Schema {
fields := make(map[string]*schema.Schema)
for _, f := range metaFields {
fieldSchema := &schema.Schema{
Optional: true,
ForceNew: true,
// TODO: Fields that arent in configuration shouldnt show up in resource data
// ConflictsWith: []string{f.NameInDynamicForFeed},
}
f.SchemaBuilder(fieldSchema)
fields[f.NameInDynamic] = fieldSchema
// Add an "_feed"-suffixed field for the {"feed":...} value.
fields[f.NameInDynamicForFeed] = &schema.Schema{
Optional: true,
ForceNew: true,
// TODO: Fields that arent in configuration shouldnt show up in resource data
// ConflictsWith: []string{f.NameInDynamic},
Type: schema.TypeString,
}
}
metaSchemaInner := &schema.Resource{
Schema: fields,
}
// Wrap it in a list because that seems to be the only way to have nested structs.
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: metaSchemaInner,
}
}
var metaSchema *schema.Schema = makeMetaSchema()
func metaStructToDynamic(m *data.Meta) interface{} {
d := make(map[string]interface{})
mr := reflect.ValueOf(m).Elem()
for _, f := range metaFields {
fr := mr.Field(f.StructIndex)
fv := fr.Interface()
if fv == nil {
continue
}
if mapVal, isMap := fv.(map[string]interface{}); isMap {
if len(mapVal) == 1 {
if feedVal, ok := mapVal["feed"]; ok {
if feedStr, ok := feedVal.(string); ok {
d[f.NameInDynamicForFeed] = feedStr
continue
}
}
}
panic(fmt.Sprintf("expecting feed dict, got %+v", mapVal))
}
d[f.NameInDynamic] = fv
}
return []interface{}{d}
}
func metaDynamicToStruct(m *data.Meta, raw interface{}) {
l := raw.([]interface{})
if len(l) > 1 {
panic(fmt.Sprintf("list too long %#v", l))
}
if len(l) == 0 {
return
}
if l[0] == nil {
return
}
d := l[0].(map[string]interface{})
mr := reflect.ValueOf(m).Elem()
for _, f := range metaFields {
val, present := d[f.NameInDynamic]
if present {
fr := mr.Field(f.StructIndex)
fr.Set(reflect.ValueOf(val))
}
feed, present := d[f.NameInDynamicForFeed]
if present && feed != "" {
if feed == nil {
panic("unexpected nil")
}
fr := mr.Field(f.StructIndex)
fr.Set(reflect.ValueOf(map[string]interface{}{"feed": feed.(string)}))
}
}
}

View File

@ -0,0 +1,184 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
func addPermsSchema(s map[string]*schema.Schema) map[string]*schema.Schema {
s["dns_view_zones"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["dns_manage_zones"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["dns_zones_allow_by_default"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["dns_zones_deny"] = &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
}
s["dns_zones_allow"] = &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
}
s["data_push_to_datafeeds"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["data_manage_datasources"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["data_manage_datafeeds"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_users"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_payment_methods"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_plan"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_teams"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_apikeys"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_manage_account_settings"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_view_activity_log"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["account_view_invoices"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["monitoring_manage_lists"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["monitoring_manage_jobs"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
s["monitoring_view_jobs"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
}
return s
}
func permissionsToResourceData(d *schema.ResourceData, permissions account.PermissionsMap) {
d.Set("dns_view_zones", permissions.DNS.ViewZones)
d.Set("dns_manage_zones", permissions.DNS.ManageZones)
d.Set("dns_zones_allow_by_default", permissions.DNS.ZonesAllowByDefault)
d.Set("dns_zones_deny", permissions.DNS.ZonesDeny)
d.Set("dns_zones_allow", permissions.DNS.ZonesAllow)
d.Set("data_push_to_datafeeds", permissions.Data.PushToDatafeeds)
d.Set("data_manage_datasources", permissions.Data.ManageDatasources)
d.Set("data_manage_datafeeds", permissions.Data.ManageDatafeeds)
d.Set("account_manage_users", permissions.Account.ManageUsers)
d.Set("account_manage_payment_methods", permissions.Account.ManagePaymentMethods)
d.Set("account_manage_plan", permissions.Account.ManagePlan)
d.Set("account_manage_teams", permissions.Account.ManageTeams)
d.Set("account_manage_apikeys", permissions.Account.ManageApikeys)
d.Set("account_manage_account_settings", permissions.Account.ManageAccountSettings)
d.Set("account_view_activity_log", permissions.Account.ViewActivityLog)
d.Set("account_view_invoices", permissions.Account.ViewInvoices)
d.Set("monitoring_manage_lists", permissions.Monitoring.ManageLists)
d.Set("monitoring_manage_jobs", permissions.Monitoring.ManageJobs)
d.Set("monitoring_view_jobs", permissions.Monitoring.ViewJobs)
}
func resourceDataToPermissions(d *schema.ResourceData) account.PermissionsMap {
var p account.PermissionsMap
if v, ok := d.GetOk("dns_view_zones"); ok {
p.DNS.ViewZones = v.(bool)
}
if v, ok := d.GetOk("dns_manage_zones"); ok {
p.DNS.ManageZones = v.(bool)
}
if v, ok := d.GetOk("dns_zones_allow_by_default"); ok {
p.DNS.ZonesAllowByDefault = v.(bool)
}
if v, ok := d.GetOk("dns_zones_deny"); ok {
denyRaw := v.([]interface{})
p.DNS.ZonesDeny = make([]string, len(denyRaw))
for i, deny := range denyRaw {
p.DNS.ZonesDeny[i] = deny.(string)
}
} else {
p.DNS.ZonesDeny = make([]string, 0)
}
if v, ok := d.GetOk("dns_zones_allow"); ok {
allowRaw := v.([]interface{})
p.DNS.ZonesAllow = make([]string, len(allowRaw))
for i, allow := range allowRaw {
p.DNS.ZonesAllow[i] = allow.(string)
}
} else {
p.DNS.ZonesAllow = make([]string, 0)
}
if v, ok := d.GetOk("data_push_to_datafeeds"); ok {
p.Data.PushToDatafeeds = v.(bool)
}
if v, ok := d.GetOk("data_manage_datasources"); ok {
p.Data.ManageDatasources = v.(bool)
}
if v, ok := d.GetOk("data_manage_datafeeds"); ok {
p.Data.ManageDatafeeds = v.(bool)
}
if v, ok := d.GetOk("account_manage_users"); ok {
p.Account.ManageUsers = v.(bool)
}
if v, ok := d.GetOk("account_manage_payment_methods"); ok {
p.Account.ManagePaymentMethods = v.(bool)
}
if v, ok := d.GetOk("account_manage_plan"); ok {
p.Account.ManagePlan = v.(bool)
}
if v, ok := d.GetOk("account_manage_teams"); ok {
p.Account.ManageTeams = v.(bool)
}
if v, ok := d.GetOk("account_manage_apikeys"); ok {
p.Account.ManageApikeys = v.(bool)
}
if v, ok := d.GetOk("account_manage_account_settings"); ok {
p.Account.ManageAccountSettings = v.(bool)
}
if v, ok := d.GetOk("account_view_activity_log"); ok {
p.Account.ViewActivityLog = v.(bool)
}
if v, ok := d.GetOk("account_view_invoices"); ok {
p.Account.ViewInvoices = v.(bool)
}
if v, ok := d.GetOk("monitoring_manage_lists"); ok {
p.Monitoring.ManageLists = v.(bool)
}
if v, ok := d.GetOk("monitoring_manage_jobs"); ok {
p.Monitoring.ManageJobs = v.(bool)
}
if v, ok := d.GetOk("monitoring_view_jobs"); ok {
p.Monitoring.ViewJobs = v.(bool)
}
return p
}

View File

@ -0,0 +1,50 @@
package ns1
import (
"net/http"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
)
// Provider returns a terraform.ResourceProvider.
func Provider() terraform.ResourceProvider {
return &schema.Provider{
Schema: map[string]*schema.Schema{
"apikey": &schema.Schema{
Type: schema.TypeString,
Required: true,
DefaultFunc: schema.EnvDefaultFunc("NS1_APIKEY", nil),
Description: descriptions["api_key"],
},
},
ResourcesMap: map[string]*schema.Resource{
"ns1_zone": zoneResource(),
"ns1_record": recordResource(),
"ns1_datasource": dataSourceResource(),
"ns1_datafeed": dataFeedResource(),
"ns1_monitoringjob": monitoringJobResource(),
"ns1_user": userResource(),
"ns1_apikey": apikeyResource(),
"ns1_team": teamResource(),
},
ConfigureFunc: ns1Configure,
}
}
func ns1Configure(d *schema.ResourceData) (interface{}, error) {
httpClient := &http.Client{}
n := ns1.NewClient(httpClient, ns1.SetAPIKey(d.Get("apikey").(string)))
n.RateLimitStrategySleep()
return n, nil
}
var descriptions map[string]string
func init() {
descriptions = map[string]string{
"api_key": "The ns1 API key, this is required",
}
}

View File

@ -0,0 +1,35 @@
package ns1
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{
"ns1": 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("NS1_APIKEY"); v == "" {
t.Fatal("NS1_APIKEY must be set for acceptance tests")
}
}

View File

@ -0,0 +1,109 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
func apikeyResource() *schema.Resource {
s := map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"key": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"teams": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
}
s = addPermsSchema(s)
return &schema.Resource{
Schema: s,
Create: ApikeyCreate,
Read: ApikeyRead,
Update: ApikeyUpdate,
Delete: ApikeyDelete,
}
}
func apikeyToResourceData(d *schema.ResourceData, k *account.APIKey) error {
d.SetId(k.ID)
d.Set("name", k.Name)
d.Set("key", k.Key)
d.Set("teams", k.TeamIDs)
permissionsToResourceData(d, k.Permissions)
return nil
}
func resourceDataToApikey(k *account.APIKey, d *schema.ResourceData) error {
k.ID = d.Id()
k.Name = d.Get("name").(string)
if v, ok := d.GetOk("teams"); ok {
teamsRaw := v.([]interface{})
k.TeamIDs = make([]string, len(teamsRaw))
for i, team := range teamsRaw {
k.TeamIDs[i] = team.(string)
}
} else {
k.TeamIDs = make([]string, 0)
}
k.Permissions = resourceDataToPermissions(d)
return nil
}
// ApikeyCreate creates ns1 API key
func ApikeyCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
k := account.APIKey{}
if err := resourceDataToApikey(&k, d); err != nil {
return err
}
if _, err := client.APIKeys.Create(&k); err != nil {
return err
}
return apikeyToResourceData(d, &k)
}
// ApikeyRead reads API key from ns1
func ApikeyRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
k, _, err := client.APIKeys.Get(d.Id())
if err != nil {
return err
}
return apikeyToResourceData(d, k)
}
//ApikeyDelete deletes the given ns1 api key
func ApikeyDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.APIKeys.Delete(d.Id())
d.SetId("")
return err
}
//ApikeyUpdate updates the given api key in ns1
func ApikeyUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
k := account.APIKey{
ID: d.Id(),
}
if err := resourceDataToApikey(&k, d); err != nil {
return err
}
if _, err := client.APIKeys.Update(&k); err != nil {
return err
}
return apikeyToResourceData(d, &k)
}

View File

@ -0,0 +1,92 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
func dataFeedResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"source_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"config": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
},
Create: DataFeedCreate,
Read: DataFeedRead,
Update: DataFeedUpdate,
Delete: DataFeedDelete,
}
}
func dataFeedToResourceData(d *schema.ResourceData, f *data.Feed) {
d.SetId(f.ID)
d.Set("name", f.Name)
d.Set("config", f.Config)
}
func resourceDataToDataFeed(d *schema.ResourceData) *data.Feed {
return &data.Feed{
Name: d.Get("name").(string),
SourceID: d.Get("source_id").(string),
Config: d.Get("config").(map[string]interface{}),
}
}
// DataFeedCreate creates an ns1 datafeed
func DataFeedCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
f := resourceDataToDataFeed(d)
if _, err := client.DataFeeds.Create(d.Get("source_id").(string), f); err != nil {
return err
}
dataFeedToResourceData(d, f)
return nil
}
// DataFeedRead reads the datafeed for the given ID from ns1
func DataFeedRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
f, _, err := client.DataFeeds.Get(d.Get("source_id").(string), d.Id())
if err != nil {
return err
}
dataFeedToResourceData(d, f)
return nil
}
// DataFeedDelete delets the given datafeed from ns1
func DataFeedDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.DataFeeds.Delete(d.Get("source_id").(string), d.Id())
d.SetId("")
return err
}
// DataFeedUpdate updates the given datafeed with modified parameters
func DataFeedUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
f := resourceDataToDataFeed(d)
f.ID = d.Id()
if _, err := client.DataFeeds.Update(d.Get("source_id").(string), f); err != nil {
return err
}
dataFeedToResourceData(d, f)
return nil
}

View File

@ -0,0 +1,170 @@
package ns1
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
func TestAccDataFeed_basic(t *testing.T) {
var dataFeed data.Feed
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDataFeedDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataFeedBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataFeedExists("ns1_datafeed.foobar", "ns1_datasource.api", &dataFeed),
testAccCheckDataFeedName(&dataFeed, "terraform test"),
testAccCheckDataFeedConfig(&dataFeed, "label", "exampledc2"),
),
},
},
})
}
func TestAccDataFeed_updated(t *testing.T) {
var dataFeed data.Feed
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDataFeedDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataFeedBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataFeedExists("ns1_datafeed.foobar", "ns1_datasource.api", &dataFeed),
testAccCheckDataFeedName(&dataFeed, "terraform test"),
testAccCheckDataFeedConfig(&dataFeed, "label", "exampledc2"),
),
},
resource.TestStep{
Config: testAccDataFeedUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataFeedExists("ns1_datafeed.foobar", "ns1_datasource.api", &dataFeed),
testAccCheckDataFeedName(&dataFeed, "terraform test"),
testAccCheckDataFeedConfig(&dataFeed, "label", "exampledc3"),
),
},
},
})
}
func testAccCheckDataFeedExists(n string, dsrc string, dataFeed *data.Feed) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
ds, ok := s.RootModule().Resources[dsrc]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("NoID is set")
}
if ds.Primary.ID == "" {
return fmt.Errorf("NoID is set for the datasource")
}
client := testAccProvider.Meta().(*ns1.Client)
foundFeed, _, err := client.DataFeeds.Get(ds.Primary.Attributes["id"], rs.Primary.Attributes["id"])
p := rs.Primary
if err != nil {
return err
}
if foundFeed.Name != p.Attributes["name"] {
return fmt.Errorf("DataFeed not found")
}
*dataFeed = *foundFeed
return nil
}
}
func testAccCheckDataFeedDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
var dataFeedID string
var dataSourceID string
for _, rs := range s.RootModule().Resources {
if rs.Type == "ns1_datasource" {
dataSourceID = rs.Primary.Attributes["id"]
}
if rs.Type == "ns1_datafeed" {
dataFeedID = rs.Primary.Attributes["id"]
}
}
df, _, _ := client.DataFeeds.Get(dataSourceID, dataFeedID)
if df != nil {
return fmt.Errorf("DataFeed still exists: %#v", df)
}
return nil
}
func testAccCheckDataFeedName(dataFeed *data.Feed, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if dataFeed.Name != expected {
return fmt.Errorf("Name: got: %#v want: %#v", dataFeed.Name, expected)
}
return nil
}
}
func testAccCheckDataFeedConfig(dataFeed *data.Feed, key, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if dataFeed.Config[key] != expected {
return fmt.Errorf("Config[%s]: got: %#v, want: %s", key, dataFeed.Config[key], expected)
}
return nil
}
}
const testAccDataFeedBasic = `
resource "ns1_datasource" "api" {
name = "terraform test"
sourcetype = "nsone_v1"
}
resource "ns1_datafeed" "foobar" {
name = "terraform test"
source_id = "${ns1_datasource.api.id}"
config {
label = "exampledc2"
}
}`
const testAccDataFeedUpdated = `
resource "ns1_datasource" "api" {
name = "terraform test"
sourcetype = "nsone_v1"
}
resource "ns1_datafeed" "foobar" {
name = "terraform test"
source_id = "${ns1_datasource.api.id}"
config {
label = "exampledc3"
}
}`

View File

@ -0,0 +1,86 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
func dataSourceResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"sourcetype": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"config": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
},
Create: DataSourceCreate,
Read: DataSourceRead,
Update: DataSourceUpdate,
Delete: DataSourceDelete,
}
}
func dataSourceToResourceData(d *schema.ResourceData, s *data.Source) {
d.SetId(s.ID)
d.Set("name", s.Name)
d.Set("sourcetype", s.Type)
d.Set("config", s.Config)
}
// DataSourceCreate creates an ns1 datasource
func DataSourceCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
s := data.NewSource(d.Get("name").(string), d.Get("sourcetype").(string))
s.Config = d.Get("config").(map[string]interface{})
if _, err := client.DataSources.Create(s); err != nil {
return err
}
dataSourceToResourceData(d, s)
return nil
}
// DataSourceRead fetches info for the given datasource from ns1
func DataSourceRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
s, _, err := client.DataSources.Get(d.Id())
if err != nil {
return err
}
dataSourceToResourceData(d, s)
return nil
}
// DataSourceDelete deteltes the given datasource from ns1
func DataSourceDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.DataSources.Delete(d.Id())
d.SetId("")
return err
}
// DataSourceUpdate updates the datasource with given parameters
func DataSourceUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
s := data.NewSource(d.Get("name").(string), d.Get("sourcetype").(string))
s.ID = d.Id()
if _, err := client.DataSources.Update(s); err != nil {
return err
}
dataSourceToResourceData(d, s)
return nil
}

View File

@ -0,0 +1,140 @@
package ns1
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
func TestAccDataSource_basic(t *testing.T) {
var dataSource data.Source
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDataSourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataSourceBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSourceExists("ns1_datasource.foobar", &dataSource),
testAccCheckDataSourceName(&dataSource, "terraform test"),
testAccCheckDataSourceType(&dataSource, "nsone_v1"),
),
},
},
})
}
func TestAccDataSource_updated(t *testing.T) {
var dataSource data.Source
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckDataSourceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataSourceBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSourceExists("ns1_datasource.foobar", &dataSource),
testAccCheckDataSourceName(&dataSource, "terraform test"),
testAccCheckDataSourceType(&dataSource, "nsone_v1"),
),
},
resource.TestStep{
Config: testAccDataSourceUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSourceExists("ns1_datasource.foobar", &dataSource),
testAccCheckDataSourceName(&dataSource, "terraform test"),
testAccCheckDataSourceType(&dataSource, "nsone_monitoring"),
),
},
},
})
}
func testAccCheckDataSourceExists(n string, dataSource *data.Source) 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("NoID is set")
}
client := testAccProvider.Meta().(*ns1.Client)
foundSource, _, err := client.DataSources.Get(rs.Primary.Attributes["id"])
p := rs.Primary
if err != nil {
return err
}
if foundSource.Name != p.Attributes["name"] {
return fmt.Errorf("Datasource not found")
}
*dataSource = *foundSource
return nil
}
}
func testAccCheckDataSourceDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "ns1_datasource" {
continue
}
_, _, err := client.DataSources.Get(rs.Primary.Attributes["id"])
if err == nil {
return fmt.Errorf("Datasource still exists")
}
}
return nil
}
func testAccCheckDataSourceName(dataSource *data.Source, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if dataSource.Name != expected {
return fmt.Errorf("Name: got: %#v want: %#v", dataSource.Name, expected)
}
return nil
}
}
func testAccCheckDataSourceType(dataSource *data.Source, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if dataSource.Type != expected {
return fmt.Errorf("Type: got: %#v want: %#v", dataSource.Type, expected)
}
return nil
}
}
const testAccDataSourceBasic = `
resource "ns1_datasource" "foobar" {
name = "terraform test"
sourcetype = "nsone_v1"
}`
const testAccDataSourceUpdated = `
resource "ns1_datasource" "foobar" {
name = "terraform test"
sourcetype = "nsone_monitoring"
}`

View File

@ -0,0 +1,297 @@
package ns1
import (
"fmt"
"regexp"
"strconv"
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/monitor"
)
func monitoringJobResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
// Required
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"job_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"regions": &schema.Schema{
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"frequency": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"config": &schema.Schema{
Type: schema.TypeMap,
Required: true,
},
// Optional
"active": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"rapid_recheck": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"policy": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Default: "quorum",
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) {
value := v.(string)
if !regexp.MustCompile(`^(all|one|quorum)$`).MatchString(value) {
es = append(es, fmt.Errorf(
"only all, one, quorum allowed in %q", k))
}
return
},
},
"notes": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"notify_delay": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"notify_repeat": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"notify_failback": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"notify_regional": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"notify_list": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"rules": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"value": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"comparison": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"key": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
},
},
},
// Computed
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
Create: MonitoringJobCreate,
Read: MonitoringJobRead,
Update: MonitoringJobUpdate,
Delete: MonitoringJobDelete,
}
}
func monitoringJobToResourceData(d *schema.ResourceData, r *monitor.Job) error {
d.SetId(r.ID)
d.Set("name", r.Name)
d.Set("job_type", r.Type)
d.Set("active", r.Active)
d.Set("regions", r.Regions)
d.Set("frequency", r.Frequency)
d.Set("rapid_recheck", r.RapidRecheck)
config := make(map[string]string)
for k, v := range r.Config {
if k == "ssl" {
if v.(bool) {
config[k] = "1"
} else {
config[k] = "0"
}
} else {
switch t := v.(type) {
case string:
config[k] = t
case float64:
config[k] = strconv.FormatFloat(t, 'f', -1, 64)
}
}
}
err := d.Set("config", config)
if err != nil {
panic(fmt.Errorf("[DEBUG] Error setting Config error: %#v %#v", r.Config, err))
}
d.Set("policy", r.Policy)
d.Set("notes", r.Notes)
d.Set("frequency", r.Frequency)
d.Set("notify_delay", r.NotifyDelay)
d.Set("notify_repeat", r.NotifyRepeat)
d.Set("notify_regional", r.NotifyRegional)
d.Set("notify_failback", r.NotifyFailback)
d.Set("notify_list", r.NotifyListID)
if len(r.Rules) > 0 {
rules := make([]map[string]interface{}, len(r.Rules))
for i, r := range r.Rules {
m := make(map[string]interface{})
m["value"] = r.Value
m["comparison"] = r.Comparison
m["key"] = r.Key
rules[i] = m
}
}
return nil
}
func resourceDataToMonitoringJob(r *monitor.Job, d *schema.ResourceData) error {
r.ID = d.Id()
r.Name = d.Get("name").(string)
r.Type = d.Get("job_type").(string)
r.Active = d.Get("active").(bool)
rawRegions := d.Get("regions").([]interface{})
r.Regions = make([]string, len(rawRegions))
for i, v := range rawRegions {
r.Regions[i] = v.(string)
}
r.Frequency = d.Get("frequency").(int)
r.RapidRecheck = d.Get("rapid_recheck").(bool)
var rawRules []interface{}
if rawRules := d.Get("rules"); rawRules != nil {
r.Rules = make([]*monitor.Rule, len(rawRules.([]interface{})))
for i, v := range rawRules.([]interface{}) {
rule := v.(map[string]interface{})
r.Rules[i] = &monitor.Rule{
Value: rule["value"].(string),
Comparison: rule["comparison"].(string),
Key: rule["key"].(string),
}
}
} else {
r.Rules = make([]*monitor.Rule, 0)
}
for i, v := range rawRules {
rule := v.(map[string]interface{})
r.Rules[i] = &monitor.Rule{
Comparison: rule["comparison"].(string),
Key: rule["key"].(string),
}
value := rule["value"].(string)
if i, err := strconv.Atoi(value); err == nil {
r.Rules[i].Value = i
} else {
r.Rules[i].Value = value
}
}
config := make(map[string]interface{})
if rawConfig := d.Get("config"); rawConfig != nil {
for k, v := range rawConfig.(map[string]interface{}) {
if k == "ssl" {
if v.(string) == "1" {
config[k] = true
}
} else {
if i, err := strconv.Atoi(v.(string)); err == nil {
config[k] = i
} else {
config[k] = v
}
}
}
}
r.Config = config
r.RegionScope = "fixed"
r.Policy = d.Get("policy").(string)
if v, ok := d.GetOk("notes"); ok {
r.Notes = v.(string)
}
r.Frequency = d.Get("frequency").(int)
if v, ok := d.GetOk("notify_delay"); ok {
r.NotifyDelay = v.(int)
}
if v, ok := d.GetOk("notify_repeat"); ok {
r.NotifyRepeat = v.(int)
}
if v, ok := d.GetOk("notify_regional"); ok {
r.NotifyRegional = v.(bool)
}
if v, ok := d.GetOk("notify_failback"); ok {
r.NotifyFailback = v.(bool)
}
if v, ok := d.GetOk("notify_list"); ok {
r.NotifyListID = v.(string)
}
return nil
}
// MonitoringJobCreate Creates monitoring job in ns1
func MonitoringJobCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
j := monitor.Job{}
if err := resourceDataToMonitoringJob(&j, d); err != nil {
return err
}
if _, err := client.Jobs.Create(&j); err != nil {
return err
}
return monitoringJobToResourceData(d, &j)
}
// MonitoringJobRead reads the given monitoring job from ns1
func MonitoringJobRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
j, _, err := client.Jobs.Get(d.Id())
if err != nil {
return err
}
return monitoringJobToResourceData(d, j)
}
// MonitoringJobDelete deteltes the given monitoring job from ns1
func MonitoringJobDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.Jobs.Delete(d.Id())
d.SetId("")
return err
}
// MonitoringJobUpdate updates the given monitoring job
func MonitoringJobUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
j := monitor.Job{
ID: d.Id(),
}
if err := resourceDataToMonitoringJob(&j, d); err != nil {
return err
}
if _, err := client.Jobs.Update(&j); err != nil {
return err
}
return monitoringJobToResourceData(d, &j)
}

View File

@ -0,0 +1,278 @@
package ns1
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/monitor"
)
func TestAccMonitoringJob_basic(t *testing.T) {
var mj monitor.Job
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckMonitoringJobDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccMonitoringJobBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckMonitoringJobExists("ns1_monitoringjob.it", &mj),
testAccCheckMonitoringJobName(&mj, "terraform test"),
testAccCheckMonitoringJobActive(&mj, true),
testAccCheckMonitoringJobRegions(&mj, []string{"lga"}),
testAccCheckMonitoringJobType(&mj, "tcp"),
testAccCheckMonitoringJobFrequency(&mj, 60),
testAccCheckMonitoringJobRapidRecheck(&mj, false),
testAccCheckMonitoringJobPolicy(&mj, "quorum"),
testAccCheckMonitoringJobConfigSend(&mj, "HEAD / HTTP/1.0\r\n\r\n"),
testAccCheckMonitoringJobConfigPort(&mj, 80),
testAccCheckMonitoringJobConfigHost(&mj, "1.1.1.1"),
),
},
},
})
}
func TestAccMonitoringJob_updated(t *testing.T) {
var mj monitor.Job
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckMonitoringJobDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccMonitoringJobBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckMonitoringJobExists("ns1_monitoringjob.it", &mj),
testAccCheckMonitoringJobName(&mj, "terraform test"),
testAccCheckMonitoringJobActive(&mj, true),
testAccCheckMonitoringJobRegions(&mj, []string{"lga"}),
testAccCheckMonitoringJobType(&mj, "tcp"),
testAccCheckMonitoringJobFrequency(&mj, 60),
testAccCheckMonitoringJobRapidRecheck(&mj, false),
testAccCheckMonitoringJobPolicy(&mj, "quorum"),
testAccCheckMonitoringJobConfigSend(&mj, "HEAD / HTTP/1.0\r\n\r\n"),
testAccCheckMonitoringJobConfigPort(&mj, 80),
testAccCheckMonitoringJobConfigHost(&mj, "1.1.1.1"),
),
},
resource.TestStep{
Config: testAccMonitoringJobUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckMonitoringJobExists("ns1_monitoringjob.it", &mj),
testAccCheckMonitoringJobName(&mj, "terraform test"),
testAccCheckMonitoringJobActive(&mj, true),
testAccCheckMonitoringJobRegions(&mj, []string{"lga"}),
testAccCheckMonitoringJobType(&mj, "tcp"),
testAccCheckMonitoringJobFrequency(&mj, 120),
testAccCheckMonitoringJobRapidRecheck(&mj, true),
testAccCheckMonitoringJobPolicy(&mj, "all"),
testAccCheckMonitoringJobConfigSend(&mj, "HEAD / HTTP/1.0\r\n\r\n"),
testAccCheckMonitoringJobConfigPort(&mj, 443),
testAccCheckMonitoringJobConfigHost(&mj, "1.1.1.1"),
),
},
},
})
}
func testAccCheckMonitoringJobState(key, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources["ns1_monitoringjob.it"]
if !ok {
return fmt.Errorf("Not found: %s", "ns1_monitoringjob.it")
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
p := rs.Primary
if p.Attributes[key] != value {
return fmt.Errorf(
"%s != %s (actual: %s)", key, value, p.Attributes[key])
}
return nil
}
}
func testAccCheckMonitoringJobExists(n string, monitoringJob *monitor.Job) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Resource not found: %v", n)
}
id := rs.Primary.ID
if id == "" {
return fmt.Errorf("ID is not set")
}
client := testAccProvider.Meta().(*ns1.Client)
foundMj, _, err := client.Jobs.Get(id)
if err != nil {
return err
}
if foundMj.ID != id {
return fmt.Errorf("Monitoring Job not found want: %#v, got %#v", id, foundMj)
}
*monitoringJob = *foundMj
return nil
}
}
func testAccCheckMonitoringJobDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "ns1_monitoringjob" {
continue
}
mj, _, err := client.Jobs.Get(rs.Primary.Attributes["id"])
if err == nil {
return fmt.Errorf("Monitoring Job still exists %#v: %#v", err, mj)
}
}
return nil
}
func testAccCheckMonitoringJobName(mj *monitor.Job, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Name != expected {
return fmt.Errorf("Name: got: %#v want: %#v", mj.Name, expected)
}
return nil
}
}
func testAccCheckMonitoringJobActive(mj *monitor.Job, expected bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Active != expected {
return fmt.Errorf("Active: got: %#v want: %#v", mj.Active, expected)
}
return nil
}
}
func testAccCheckMonitoringJobRegions(mj *monitor.Job, expected []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if !reflect.DeepEqual(mj.Regions, expected) {
return fmt.Errorf("Regions: got: %#v want: %#v", mj.Regions, expected)
}
return nil
}
}
func testAccCheckMonitoringJobType(mj *monitor.Job, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Type != expected {
return fmt.Errorf("Type: got: %#v want: %#v", mj.Type, expected)
}
return nil
}
}
func testAccCheckMonitoringJobFrequency(mj *monitor.Job, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Frequency != expected {
return fmt.Errorf("Frequency: got: %#v want: %#v", mj.Frequency, expected)
}
return nil
}
}
func testAccCheckMonitoringJobRapidRecheck(mj *monitor.Job, expected bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.RapidRecheck != expected {
return fmt.Errorf("RapidRecheck: got: %#v want: %#v", mj.RapidRecheck, expected)
}
return nil
}
}
func testAccCheckMonitoringJobPolicy(mj *monitor.Job, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Policy != expected {
return fmt.Errorf("Policy: got: %#v want: %#v", mj.Policy, expected)
}
return nil
}
}
func testAccCheckMonitoringJobConfigSend(mj *monitor.Job, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Config["send"].(string) != expected {
return fmt.Errorf("Config.send: got: %#v want: %#v", mj.Config["send"].(string), expected)
}
return nil
}
}
func testAccCheckMonitoringJobConfigPort(mj *monitor.Job, expected float64) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Config["port"].(float64) != expected {
return fmt.Errorf("Config.port: got: %#v want: %#v", mj.Config["port"].(float64), expected)
}
return nil
}
}
func testAccCheckMonitoringJobConfigHost(mj *monitor.Job, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if mj.Config["host"].(string) != expected {
return fmt.Errorf("Config.host: got: %#v want: %#v", mj.Config["host"].(string), expected)
}
return nil
}
}
const testAccMonitoringJobBasic = `
resource "ns1_monitoringjob" "it" {
job_type = "tcp"
name = "terraform test"
regions = ["lga"]
frequency = 60
config {
send = "HEAD / HTTP/1.0\r\n\r\n"
port = 80
host = "1.1.1.1"
}
}
`
const testAccMonitoringJobUpdated = `
resource "ns1_monitoringjob" "it" {
job_type = "tcp"
name = "terraform test"
active = true
regions = ["lga"]
frequency = 120
rapid_recheck = true
policy = "all"
config {
send = "HEAD / HTTP/1.0\r\n\r\n"
port = 443
host = "1.1.1.1"
}
}
`

View File

@ -0,0 +1,367 @@
package ns1
import (
"errors"
"fmt"
"log"
"strconv"
"strings"
"github.com/hashicorp/terraform/helper/schema"
"github.com/mitchellh/hashstructure"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
"gopkg.in/ns1/ns1-go.v2/rest/model/filter"
)
var recordTypeStringEnum *StringEnum = NewStringEnum([]string{
"A",
"AAAA",
"ALIAS",
"AFSDB",
"CNAME",
"DNAME",
"HINFO",
"MX",
"NAPTR",
"NS",
"PTR",
"RP",
"SPF",
"SRV",
"TXT",
})
func recordResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
// Required
"zone": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"domain": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"type": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: recordTypeStringEnum.ValidateFunc,
},
// Optional
"ttl": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
// "meta": metaSchema,
"link": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"use_client_subnet": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"answers": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"answer": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
// "meta": metaSchema,
},
},
Set: genericHasher,
},
"regions": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
// "meta": metaSchema,
},
},
Set: genericHasher,
},
"filters": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"filter": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"disabled": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
"config": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
},
},
},
// Computed
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
Create: RecordCreate,
Read: RecordRead,
Update: RecordUpdate,
Delete: RecordDelete,
Importer: &schema.ResourceImporter{State: RecordStateFunc},
}
}
func genericHasher(v interface{}) int {
hash, err := hashstructure.Hash(v, nil)
if err != nil {
panic(fmt.Sprintf("error computing hash code for %#v: %s", v, err.Error()))
}
return int(hash)
}
func recordToResourceData(d *schema.ResourceData, r *dns.Record) error {
d.SetId(r.ID)
d.Set("domain", r.Domain)
d.Set("zone", r.Zone)
d.Set("type", r.Type)
d.Set("ttl", r.TTL)
if r.Link != "" {
d.Set("link", r.Link)
}
// if r.Meta != nil {
// d.State()
// t := metaStructToDynamic(r.Meta)
// d.Set("meta", t)
// }
if len(r.Filters) > 0 {
filters := make([]map[string]interface{}, len(r.Filters))
for i, f := range r.Filters {
m := make(map[string]interface{})
m["filter"] = f.Type
if f.Disabled {
m["disabled"] = true
}
if f.Config != nil {
m["config"] = f.Config
}
filters[i] = m
}
d.Set("filters", filters)
}
if len(r.Answers) > 0 {
ans := &schema.Set{
F: genericHasher,
}
log.Printf("Got back from ns1 answers: %+v", r.Answers)
for _, answer := range r.Answers {
ans.Add(answerToMap(*answer))
}
log.Printf("Setting answers %+v", ans)
err := d.Set("answers", ans)
if err != nil {
return fmt.Errorf("[DEBUG] Error setting answers for: %s, error: %#v", r.Domain, err)
}
}
if len(r.Regions) > 0 {
regions := make([]map[string]interface{}, 0, len(r.Regions))
for regionName, _ := range r.Regions {
newRegion := make(map[string]interface{})
newRegion["name"] = regionName
// newRegion["meta"] = metaStructToDynamic(&region.Meta)
regions = append(regions, newRegion)
}
log.Printf("Setting regions %+v", regions)
err := d.Set("regions", regions)
if err != nil {
return fmt.Errorf("[DEBUG] Error setting regions for: %s, error: %#v", r.Domain, err)
}
}
return nil
}
func answerToMap(a dns.Answer) map[string]interface{} {
m := make(map[string]interface{})
m["answer"] = strings.Join(a.Rdata, " ")
if a.RegionName != "" {
m["region"] = a.RegionName
}
// if a.Meta != nil {
// m["meta"] = metaStructToDynamic(a.Meta)
// }
return m
}
func btoi(b bool) int {
if b {
return 1
}
return 0
}
func resourceDataToRecord(r *dns.Record, d *schema.ResourceData) error {
r.ID = d.Id()
if answers := d.Get("answers").(*schema.Set); answers.Len() > 0 {
al := make([]*dns.Answer, answers.Len())
for i, answerRaw := range answers.List() {
answer := answerRaw.(map[string]interface{})
var a *dns.Answer
v := answer["answer"].(string)
switch d.Get("type") {
case "TXT":
a = dns.NewTXTAnswer(v)
default:
a = dns.NewAnswer(strings.Split(v, " "))
}
if v, ok := answer["region"]; ok {
a.RegionName = v.(string)
}
// if v, ok := answer["meta"]; ok {
// metaDynamicToStruct(a.Meta, v)
// }
al[i] = a
}
r.Answers = al
if _, ok := d.GetOk("link"); ok {
return errors.New("Cannot have both link and answers in a record")
}
}
if v, ok := d.GetOk("ttl"); ok {
r.TTL = v.(int)
}
if v, ok := d.GetOk("link"); ok {
r.LinkTo(v.(string))
}
// if v, ok := d.GetOk("meta"); ok {
// metaDynamicToStruct(r.Meta, v)
// }
useClientSubnetVal := d.Get("use_client_subnet").(bool)
if v := strconv.FormatBool(useClientSubnetVal); v != "" {
r.UseClientSubnet = &useClientSubnetVal
}
if rawFilters := d.Get("filters").([]interface{}); len(rawFilters) > 0 {
f := make([]*filter.Filter, len(rawFilters))
for i, filterRaw := range rawFilters {
fi := filterRaw.(map[string]interface{})
config := make(map[string]interface{})
filter := filter.Filter{
Type: fi["filter"].(string),
Config: config,
}
if disabled, ok := fi["disabled"]; ok {
filter.Disabled = disabled.(bool)
}
if rawConfig, ok := fi["config"]; ok {
for k, v := range rawConfig.(map[string]interface{}) {
if i, err := strconv.Atoi(v.(string)); err == nil {
filter.Config[k] = i
} else {
filter.Config[k] = v
}
}
}
f[i] = &filter
}
r.Filters = f
}
if regions := d.Get("regions").(*schema.Set); regions.Len() > 0 {
for _, regionRaw := range regions.List() {
region := regionRaw.(map[string]interface{})
ns1R := data.Region{
Meta: data.Meta{},
}
// if v, ok := region["meta"]; ok {
// metaDynamicToStruct(&ns1R.Meta, v)
// }
r.Regions[region["name"].(string)] = ns1R
}
}
return nil
}
// RecordCreate creates DNS record in ns1
func RecordCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
r := dns.NewRecord(d.Get("zone").(string), d.Get("domain").(string), d.Get("type").(string))
if err := resourceDataToRecord(r, d); err != nil {
return err
}
if _, err := client.Records.Create(r); err != nil {
return err
}
return recordToResourceData(d, r)
}
// RecordRead reads the DNS record from ns1
func RecordRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
r, _, err := client.Records.Get(d.Get("zone").(string), d.Get("domain").(string), d.Get("type").(string))
if err != nil {
return err
}
return recordToResourceData(d, r)
}
// RecordDelete deltes the DNS record from ns1
func RecordDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.Records.Delete(d.Get("zone").(string), d.Get("domain").(string), d.Get("type").(string))
d.SetId("")
return err
}
// RecordUpdate updates the given dns record in ns1
func RecordUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
r := dns.NewRecord(d.Get("zone").(string), d.Get("domain").(string), d.Get("type").(string))
if err := resourceDataToRecord(r, d); err != nil {
return err
}
if _, err := client.Records.Update(r); err != nil {
return err
}
return recordToResourceData(d, r)
}
func RecordStateFunc(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
parts := strings.Split(d.Id(), "/")
if len(parts) != 3 {
return nil, fmt.Errorf("Invalid record specifier. Expecting 2 slashes (\"zone/domain/type\"), got %d.", len(parts)-1)
}
d.Set("zone", parts[0])
d.Set("domain", parts[1])
d.Set("type", parts[2])
return []*schema.ResourceData{d}, nil
}

View File

@ -0,0 +1,287 @@
package ns1
import (
"fmt"
"reflect"
"sort"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
func TestAccRecord_basic(t *testing.T) {
var record dns.Record
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRecordDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRecordBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckRecordExists("ns1_record.it", &record),
testAccCheckRecordDomain(&record, "test.terraform-record-test.io"),
testAccCheckRecordTTL(&record, 60),
testAccCheckRecordRegionName(&record, []string{"cal"}),
// testAccCheckRecordAnswerMetaWeight(&record, 10),
testAccCheckRecordAnswerRdata(&record, "test1.terraform-record-test.io"),
),
},
},
})
}
func TestAccRecord_updated(t *testing.T) {
var record dns.Record
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckRecordDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccRecordBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckRecordExists("ns1_record.it", &record),
testAccCheckRecordDomain(&record, "test.terraform-record-test.io"),
testAccCheckRecordTTL(&record, 60),
testAccCheckRecordRegionName(&record, []string{"cal"}),
// testAccCheckRecordAnswerMetaWeight(&record, 10),
testAccCheckRecordAnswerRdata(&record, "test1.terraform-record-test.io"),
),
},
resource.TestStep{
Config: testAccRecordUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckRecordExists("ns1_record.it", &record),
testAccCheckRecordDomain(&record, "test.terraform-record-test.io"),
testAccCheckRecordTTL(&record, 120),
testAccCheckRecordRegionName(&record, []string{"ny", "wa"}),
// testAccCheckRecordAnswerMetaWeight(&record, 5),
testAccCheckRecordAnswerRdata(&record, "test2.terraform-record-test.io"),
),
},
},
})
}
func testAccCheckRecordExists(n string, record *dns.Record) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %v", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("NoID is set")
}
client := testAccProvider.Meta().(*ns1.Client)
p := rs.Primary
foundRecord, _, err := client.Records.Get(p.Attributes["zone"], p.Attributes["domain"], p.Attributes["type"])
if err != nil {
return fmt.Errorf("Record not found")
}
if foundRecord.Domain != p.Attributes["domain"] {
return fmt.Errorf("Record not found")
}
*record = *foundRecord
return nil
}
}
func testAccCheckRecordDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
var recordDomain string
var recordZone string
var recordType string
for _, rs := range s.RootModule().Resources {
if rs.Type != "ns1_record" {
continue
}
if rs.Type == "ns1_record" {
recordType = rs.Primary.Attributes["type"]
recordDomain = rs.Primary.Attributes["domain"]
recordZone = rs.Primary.Attributes["zone"]
}
}
foundRecord, _, err := client.Records.Get(recordDomain, recordZone, recordType)
if err != nil {
return fmt.Errorf("Record still exists: %#v", foundRecord)
}
return nil
}
func testAccCheckRecordDomain(r *dns.Record, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if r.Domain != expected {
return fmt.Errorf("Domain: got: %#v want: %#v", r.Domain, expected)
}
return nil
}
}
func testAccCheckRecordTTL(r *dns.Record, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if r.TTL != expected {
return fmt.Errorf("TTL: got: %#v want: %#v", r.TTL, expected)
}
return nil
}
}
func testAccCheckRecordRegionName(r *dns.Record, expected []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
regions := make([]string, len(r.Regions))
i := 0
for k := range r.Regions {
regions[i] = k
i++
}
sort.Strings(regions)
sort.Strings(expected)
if !reflect.DeepEqual(regions, expected) {
return fmt.Errorf("Regions: got: %#v want: %#v", regions, expected)
}
return nil
}
}
func testAccCheckRecordAnswerMetaWeight(r *dns.Record, expected float64) resource.TestCheckFunc {
return func(s *terraform.State) error {
recordAnswer := r.Answers[0]
recordMetas := recordAnswer.Meta
weight := recordMetas.Weight.(float64)
if weight != expected {
return fmt.Errorf("Answers[0].Meta.Weight: got: %#v want: %#v", weight, expected)
}
return nil
}
}
func testAccCheckRecordAnswerRdata(r *dns.Record, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
recordAnswer := r.Answers[0]
recordAnswerString := recordAnswer.Rdata[0]
if recordAnswerString != expected {
return fmt.Errorf("Answers[0].Rdata[0]: got: %#v want: %#v", recordAnswerString, expected)
}
return nil
}
}
const testAccRecordBasic = `
resource "ns1_record" "it" {
zone = "${ns1_zone.test.zone}"
domain = "test.${ns1_zone.test.zone}"
type = "CNAME"
ttl = 60
// meta {
// weight = 5
// connections = 3
// // up = false // Ignored by d.GetOk("meta.0.up") due to known issue
// }
answers {
answer = "test1.terraform-record-test.io"
region = "cal"
// meta {
// weight = 10
// up = true
// }
}
regions {
name = "cal"
// meta {
// up = true
// us_state = ["CA"]
// }
}
filters {
filter = "up"
}
filters {
filter = "geotarget_country"
}
filters {
filter = "select_first_n"
config = {N=1}
}
}
resource "ns1_zone" "test" {
zone = "terraform-record-test.io"
}
`
const testAccRecordUpdated = `
resource "ns1_record" "it" {
zone = "${ns1_zone.test.zone}"
domain = "test.${ns1_zone.test.zone}"
type = "CNAME"
ttl = 120
use_client_subnet = true
// meta {
// weight = 5
// connections = 3
// // up = false // Ignored by d.GetOk("meta.0.up") due to known issue
// }
answers {
answer = "test2.terraform-record-test.io"
region = "ny"
// meta {
// weight = 5
// up = true
// }
}
regions {
name = "wa"
// meta {
// us_state = ["WA"]
// }
}
regions {
name = "ny"
// meta {
// us_state = ["NY"]
// }
}
filters {
filter = "up"
}
filters {
filter = "geotarget_country"
}
}
resource "ns1_zone" "test" {
zone = "terraform-record-test.io"
}
`

View File

@ -0,0 +1,89 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
func teamResource() *schema.Resource {
s := map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
}
s = addPermsSchema(s)
return &schema.Resource{
Schema: s,
Create: TeamCreate,
Read: TeamRead,
Update: TeamUpdate,
Delete: TeamDelete,
}
}
func teamToResourceData(d *schema.ResourceData, t *account.Team) error {
d.SetId(t.ID)
d.Set("name", t.Name)
permissionsToResourceData(d, t.Permissions)
return nil
}
func resourceDataToTeam(t *account.Team, d *schema.ResourceData) error {
t.ID = d.Id()
t.Name = d.Get("name").(string)
t.Permissions = resourceDataToPermissions(d)
return nil
}
// TeamCreate creates the given team in ns1
func TeamCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
t := account.Team{}
if err := resourceDataToTeam(&t, d); err != nil {
return err
}
if _, err := client.Teams.Create(&t); err != nil {
return err
}
return teamToResourceData(d, &t)
}
// TeamRead reads the team data from ns1
func TeamRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
t, _, err := client.Teams.Get(d.Id())
if err != nil {
return err
}
return teamToResourceData(d, t)
}
// TeamDelete deletes the given team from ns1
func TeamDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.Teams.Delete(d.Id())
d.SetId("")
return err
}
// TeamUpdate updates the given team in ns1
func TeamUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
t := account.Team{
ID: d.Id(),
}
if err := resourceDataToTeam(&t, d); err != nil {
return err
}
if _, err := client.Teams.Update(&t); err != nil {
return err
}
return teamToResourceData(d, &t)
}

View File

@ -0,0 +1,209 @@
package ns1
import (
"fmt"
"reflect"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
func TestAccTeam_basic(t *testing.T) {
var team account.Team
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTeamDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccTeamBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckTeamExists("ns1_team.foobar", &team),
testAccCheckTeamName(&team, "terraform test"),
testAccCheckTeamDNSPermission(&team, "view_zones", true),
testAccCheckTeamDNSPermission(&team, "zones_allow_by_default", true),
testAccCheckTeamDNSPermissionZones(&team, "zones_allow", []string{"mytest.zone"}),
testAccCheckTeamDNSPermissionZones(&team, "zones_deny", []string{"myother.zone"}),
testAccCheckTeamDataPermission(&team, "manage_datasources", true),
),
},
},
})
}
func TestAccTeam_updated(t *testing.T) {
var team account.Team
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckTeamDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccTeamBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckTeamExists("ns1_team.foobar", &team),
testAccCheckTeamName(&team, "terraform test"),
),
},
resource.TestStep{
Config: testAccTeamUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckTeamExists("ns1_team.foobar", &team),
testAccCheckTeamName(&team, "terraform test updated"),
testAccCheckTeamDNSPermission(&team, "view_zones", true),
testAccCheckTeamDNSPermission(&team, "zones_allow_by_default", true),
testAccCheckTeamDNSPermissionZones(&team, "zones_allow", []string{}),
testAccCheckTeamDNSPermissionZones(&team, "zones_deny", []string{}),
testAccCheckTeamDataPermission(&team, "manage_datasources", false),
),
},
},
})
}
func testAccCheckTeamExists(n string, team *account.Team) 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("NoID is set")
}
client := testAccProvider.Meta().(*ns1.Client)
foundTeam, _, err := client.Teams.Get(rs.Primary.Attributes["id"])
if err != nil {
return err
}
if foundTeam.Name != rs.Primary.Attributes["name"] {
return fmt.Errorf("Team not found")
}
*team = *foundTeam
return nil
}
}
func testAccCheckTeamDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "ns1_team" {
continue
}
team, _, err := client.Teams.Get(rs.Primary.Attributes["id"])
if err == nil {
return fmt.Errorf("Team still exists: %#v: %#v", err, team.Name)
}
}
return nil
}
func testAccCheckTeamName(team *account.Team, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if team.Name != expected {
return fmt.Errorf("Name: got: %s want: %s", team.Name, expected)
}
return nil
}
}
func testAccCheckTeamDNSPermission(team *account.Team, perm string, expected bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
dns := team.Permissions.DNS
switch perm {
case "view_zones":
if dns.ViewZones != expected {
return fmt.Errorf("DNS.ViewZones: got: %t want: %t", dns.ViewZones, expected)
}
case "manage_zones":
if dns.ManageZones != expected {
return fmt.Errorf("DNS.ManageZones: got: %t want: %t", dns.ManageZones, expected)
}
case "zones_allow_by_default":
if dns.ZonesAllowByDefault != expected {
return fmt.Errorf("DNS.ZonesAllowByDefault: got: %t want: %t", dns.ZonesAllowByDefault, expected)
}
}
return nil
}
}
func testAccCheckTeamDataPermission(team *account.Team, perm string, expected bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
data := team.Permissions.Data
switch perm {
case "push_to_datafeeds":
if data.PushToDatafeeds != expected {
return fmt.Errorf("Data.PushToDatafeeds: got: %t want: %t", data.PushToDatafeeds, expected)
}
case "manage_datasources":
if data.ManageDatasources != expected {
return fmt.Errorf("Data.ManageDatasources: got: %t want: %t", data.ManageDatasources, expected)
}
case "manage_datafeeds":
if data.ManageDatafeeds != expected {
return fmt.Errorf("Data.ManageDatafeeds: got: %t want: %t", data.ManageDatafeeds, expected)
}
}
return nil
}
}
func testAccCheckTeamDNSPermissionZones(team *account.Team, perm string, expected []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
dns := team.Permissions.DNS
switch perm {
case "zones_allow":
if !reflect.DeepEqual(dns.ZonesAllow, expected) {
return fmt.Errorf("DNS.ZonesAllow: got: %v want: %v", dns.ZonesAllow, expected)
}
case "zones_deny":
if !reflect.DeepEqual(dns.ZonesDeny, expected) {
return fmt.Errorf("DNS.ZonesDeny: got: %v want: %v", dns.ZonesDeny, expected)
}
}
return nil
}
}
const testAccTeamBasic = `
resource "ns1_team" "foobar" {
name = "terraform test"
dns_view_zones = true
dns_zones_allow_by_default = true
dns_zones_allow = ["mytest.zone"]
dns_zones_deny = ["myother.zone"]
data_manage_datasources = true
}`
const testAccTeamUpdated = `
resource "ns1_team" "foobar" {
name = "terraform test updated"
dns_view_zones = true
dns_zones_allow_by_default = true
data_manage_datasources = false
}`

View File

@ -0,0 +1,133 @@
package ns1
import (
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
func userResource() *schema.Resource {
s := map[string]*schema.Schema{
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"username": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"email": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"notify": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"billing": &schema.Schema{
Type: schema.TypeBool,
Required: true,
},
},
},
},
"teams": &schema.Schema{
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
}
s = addPermsSchema(s)
return &schema.Resource{
Schema: s,
Create: UserCreate,
Read: UserRead,
Update: UserUpdate,
Delete: UserDelete,
}
}
func userToResourceData(d *schema.ResourceData, u *account.User) error {
d.SetId(u.Username)
d.Set("name", u.Name)
d.Set("email", u.Email)
d.Set("teams", u.TeamIDs)
notify := make(map[string]bool)
notify["billing"] = u.Notify.Billing
d.Set("notify", notify)
permissionsToResourceData(d, u.Permissions)
return nil
}
func resourceDataToUser(u *account.User, d *schema.ResourceData) error {
u.Name = d.Get("name").(string)
u.Username = d.Get("username").(string)
u.Email = d.Get("email").(string)
if v, ok := d.GetOk("teams"); ok {
teamsRaw := v.([]interface{})
u.TeamIDs = make([]string, len(teamsRaw))
for i, team := range teamsRaw {
u.TeamIDs[i] = team.(string)
}
} else {
u.TeamIDs = make([]string, 0)
}
if v, ok := d.GetOk("notify"); ok {
notifyRaw := v.(map[string]interface{})
u.Notify.Billing = notifyRaw["billing"].(bool)
}
u.Permissions = resourceDataToPermissions(d)
return nil
}
// UserCreate creates the given user in ns1
func UserCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
u := account.User{}
if err := resourceDataToUser(&u, d); err != nil {
return err
}
if _, err := client.Users.Create(&u); err != nil {
return err
}
return userToResourceData(d, &u)
}
// UserRead reads the given users data from ns1
func UserRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
u, _, err := client.Users.Get(d.Id())
if err != nil {
return err
}
return userToResourceData(d, u)
}
// UserDelete deletes the given user from ns1
func UserDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.Users.Delete(d.Id())
d.SetId("")
return err
}
// UserUpdate updates the user with given parameters in ns1
func UserUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
u := account.User{
Username: d.Id(),
}
if err := resourceDataToUser(&u, d); err != nil {
return err
}
if _, err := client.Users.Update(&u); err != nil {
return err
}
return userToResourceData(d, &u)
}

View File

@ -0,0 +1,174 @@
package ns1
import (
"strings"
"github.com/hashicorp/terraform/helper/schema"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
func zoneResource() *schema.Resource {
return &schema.Resource{
Schema: map[string]*schema.Schema{
// Required
"zone": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
// Optional
"ttl": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
// SOA attributes per https://tools.ietf.org/html/rfc1035).
"refresh": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"retry": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"expiry": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
// SOA MINUMUM overloaded as NX TTL per https://tools.ietf.org/html/rfc2308
"nx_ttl": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
// TODO: test
"link": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
// TODO: test
"primary": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
// Computed
"id": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"dns_servers": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"hostmaster": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
Create: ZoneCreate,
Read: ZoneRead,
Update: ZoneUpdate,
Delete: ZoneDelete,
Importer: &schema.ResourceImporter{State: ZoneStateFunc},
}
}
func zoneToResourceData(d *schema.ResourceData, z *dns.Zone) {
d.SetId(z.ID)
d.Set("hostmaster", z.Hostmaster)
d.Set("ttl", z.TTL)
d.Set("nx_ttl", z.NxTTL)
d.Set("refresh", z.Refresh)
d.Set("retry", z.Retry)
d.Set("expiry", z.Expiry)
d.Set("dns_servers", strings.Join(z.DNSServers[:], ","))
if z.Secondary != nil && z.Secondary.Enabled {
d.Set("primary", z.Secondary.PrimaryIP)
}
if z.Link != nil && *z.Link != "" {
d.Set("link", *z.Link)
}
}
func resourceToZoneData(z *dns.Zone, d *schema.ResourceData) {
z.ID = d.Id()
if v, ok := d.GetOk("hostmaster"); ok {
z.Hostmaster = v.(string)
}
if v, ok := d.GetOk("ttl"); ok {
z.TTL = v.(int)
}
if v, ok := d.GetOk("nx_ttl"); ok {
z.NxTTL = v.(int)
}
if v, ok := d.GetOk("refresh"); ok {
z.Refresh = v.(int)
}
if v, ok := d.GetOk("retry"); ok {
z.Retry = v.(int)
}
if v, ok := d.GetOk("expiry"); ok {
z.Expiry = v.(int)
}
if v, ok := d.GetOk("primary"); ok {
z.MakeSecondary(v.(string))
}
if v, ok := d.GetOk("link"); ok {
z.LinkTo(v.(string))
}
}
// ZoneCreate creates the given zone in ns1
func ZoneCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
z := dns.NewZone(d.Get("zone").(string))
resourceToZoneData(z, d)
if _, err := client.Zones.Create(z); err != nil {
return err
}
zoneToResourceData(d, z)
return nil
}
// ZoneRead reads the given zone data from ns1
func ZoneRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
z, _, err := client.Zones.Get(d.Get("zone").(string))
if err != nil {
return err
}
zoneToResourceData(d, z)
return nil
}
// ZoneDelete deteles the given zone from ns1
func ZoneDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
_, err := client.Zones.Delete(d.Get("zone").(string))
d.SetId("")
return err
}
// ZoneUpdate updates the zone with given params in ns1
func ZoneUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ns1.Client)
z := dns.NewZone(d.Get("zone").(string))
resourceToZoneData(z, d)
if _, err := client.Zones.Update(z); err != nil {
return err
}
zoneToResourceData(d, z)
return nil
}
func ZoneStateFunc(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
d.Set("zone", d.Id())
return []*schema.ResourceData{d}, nil
}

View File

@ -0,0 +1,189 @@
package ns1
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
ns1 "gopkg.in/ns1/ns1-go.v2/rest"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
func TestAccZone_basic(t *testing.T) {
var zone dns.Zone
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckZoneDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccZoneBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckZoneExists("ns1_zone.it", &zone),
testAccCheckZoneName(&zone, "terraform-test-zone.io"),
testAccCheckZoneTTL(&zone, 3600),
testAccCheckZoneRefresh(&zone, 43200),
testAccCheckZoneRetry(&zone, 7200),
testAccCheckZoneExpiry(&zone, 1209600),
testAccCheckZoneNxTTL(&zone, 3600),
),
},
},
})
}
func TestAccZone_updated(t *testing.T) {
var zone dns.Zone
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckZoneDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccZoneBasic,
Check: resource.ComposeTestCheckFunc(
testAccCheckZoneExists("ns1_zone.it", &zone),
testAccCheckZoneName(&zone, "terraform-test-zone.io"),
testAccCheckZoneTTL(&zone, 3600),
testAccCheckZoneRefresh(&zone, 43200),
testAccCheckZoneRetry(&zone, 7200),
testAccCheckZoneExpiry(&zone, 1209600),
testAccCheckZoneNxTTL(&zone, 3600),
),
},
resource.TestStep{
Config: testAccZoneUpdated,
Check: resource.ComposeTestCheckFunc(
testAccCheckZoneExists("ns1_zone.it", &zone),
testAccCheckZoneName(&zone, "terraform-test-zone.io"),
testAccCheckZoneTTL(&zone, 10800),
testAccCheckZoneRefresh(&zone, 3600),
testAccCheckZoneRetry(&zone, 300),
testAccCheckZoneExpiry(&zone, 2592000),
testAccCheckZoneNxTTL(&zone, 3601),
),
},
},
})
}
func testAccCheckZoneExists(n string, zone *dns.Zone) 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("NoID is set")
}
client := testAccProvider.Meta().(*ns1.Client)
foundZone, _, err := client.Zones.Get(rs.Primary.Attributes["zone"])
p := rs.Primary
if err != nil {
return err
}
if foundZone.ID != p.Attributes["id"] {
return fmt.Errorf("Zone not found")
}
*zone = *foundZone
return nil
}
}
func testAccCheckZoneDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ns1.Client)
for _, rs := range s.RootModule().Resources {
if rs.Type != "ns1_zone" {
continue
}
zone, _, err := client.Zones.Get(rs.Primary.Attributes["zone"])
if err == nil {
return fmt.Errorf("Zone still exists: %#v: %#v", err, zone)
}
}
return nil
}
func testAccCheckZoneName(zone *dns.Zone, expected string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.Zone != expected {
return fmt.Errorf("Zone: got: %s want: %s", zone.Zone, expected)
}
return nil
}
}
func testAccCheckZoneTTL(zone *dns.Zone, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.TTL != expected {
return fmt.Errorf("TTL: got: %d want: %d", zone.TTL, expected)
}
return nil
}
}
func testAccCheckZoneRefresh(zone *dns.Zone, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.Refresh != expected {
return fmt.Errorf("Refresh: got: %d want: %d", zone.Refresh, expected)
}
return nil
}
}
func testAccCheckZoneRetry(zone *dns.Zone, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.Retry != expected {
return fmt.Errorf("Retry: got: %d want: %d", zone.Retry, expected)
}
return nil
}
}
func testAccCheckZoneExpiry(zone *dns.Zone, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.Expiry != expected {
return fmt.Errorf("Expiry: got: %d want: %d", zone.Expiry, expected)
}
return nil
}
}
func testAccCheckZoneNxTTL(zone *dns.Zone, expected int) resource.TestCheckFunc {
return func(s *terraform.State) error {
if zone.NxTTL != expected {
return fmt.Errorf("NxTTL: got: %d want: %d", zone.NxTTL, expected)
}
return nil
}
}
const testAccZoneBasic = `
resource "ns1_zone" "it" {
zone = "terraform-test-zone.io"
}
`
const testAccZoneUpdated = `
resource "ns1_zone" "it" {
zone = "terraform-test-zone.io"
ttl = 10800
refresh = 3600
retry = 300
expiry = 2592000
nx_ttl = 3601
# link = "1.2.3.4.in-addr.arpa" # TODO
# primary = "1.2.3.4.in-addr.arpa" # TODO
}
`

View File

@ -0,0 +1,47 @@
package ns1
import (
"fmt"
"strings"
)
type StringEnum struct {
ValueMap map[string]int
Expecting string
}
func NewStringEnum(values []string) *StringEnum {
valueMap := make(map[string]int)
quoted := make([]string, len(values), len(values))
for i, value := range values {
_, present := valueMap[value]
if present {
panic(fmt.Sprintf("duplicate value %q", value))
}
valueMap[value] = i
quoted[i] = fmt.Sprintf("%q", value)
}
return &StringEnum{
ValueMap: valueMap,
Expecting: strings.Join(quoted, ", "),
}
}
func (se *StringEnum) Check(v string) (int, error) {
i, present := se.ValueMap[v]
if present {
return i, nil
} else {
return -1, fmt.Errorf("expecting one of %s; got %q", se.Expecting, v)
}
}
func (se *StringEnum) ValidateFunc(v interface{}, k string) (ws []string, es []error) {
_, err := se.Check(v.(string))
if err != nil {
return nil, []error{err}
}
return nil, nil
}

View File

@ -40,6 +40,7 @@ import (
mysqlprovider "github.com/hashicorp/terraform/builtin/providers/mysql"
newrelicprovider "github.com/hashicorp/terraform/builtin/providers/newrelic"
nomadprovider "github.com/hashicorp/terraform/builtin/providers/nomad"
ns1provider "github.com/hashicorp/terraform/builtin/providers/ns1"
nullprovider "github.com/hashicorp/terraform/builtin/providers/null"
openstackprovider "github.com/hashicorp/terraform/builtin/providers/openstack"
opsgenieprovider "github.com/hashicorp/terraform/builtin/providers/opsgenie"
@ -108,6 +109,7 @@ var InternalProviders = map[string]plugin.ProviderFunc{
"mysql": mysqlprovider.Provider,
"newrelic": newrelicprovider.Provider,
"nomad": nomadprovider.Provider,
"ns1": ns1provider.Provider,
"null": nullprovider.Provider,
"openstack": openstackprovider.Provider,
"opsgenie": opsgenieprovider.Provider,

678
vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,678 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
APACHE HTTP SERVER SUBCOMPONENTS:
The Apache HTTP Server includes a number of subcomponents with
separate copyright notices and license terms. Your use of the source
code for the these subcomponents is subject to the terms and
conditions of the following licenses.
For the mod_mime_magic component:
/*
* mod_mime_magic: MIME type lookup via file magic numbers
* Copyright (c) 1996-1997 Cisco Systems, Inc.
*
* This software was submitted by Cisco Systems to the Apache Group in July
* 1997. Future revisions and derivatives of this source code must
* acknowledge Cisco Systems as the original contributor of this module.
* All other licensing and usage conditions are those of the Apache Group.
*
* Some of this code is derived from the free version of the file command
* originally posted to comp.sources.unix. Copyright info for that program
* is included below as required.
* ---------------------------------------------------------------------------
* - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
*
* This software is not subject to any license of the American Telephone and
* Telegraph Company or of the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on any
* computer system, and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The author is not responsible for the consequences of use of this
* software, no matter how awful, even if they arise from flaws in it.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Since few users ever read sources, credits
* must appear in the documentation.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software. Since few users ever read
* sources, credits must appear in the documentation.
*
* 4. This notice may not be removed or altered.
* -------------------------------------------------------------------------
*
*/
For the modules\mappers\mod_imap.c component:
"macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
For the server\util_md5.c component:
/************************************************************************
* NCSA HTTPd Server
* Software Development Group
* National Center for Supercomputing Applications
* University of Illinois at Urbana-Champaign
* 605 E. Springfield, Champaign, IL 61820
* httpd@ncsa.uiuc.edu
*
* Copyright (C) 1995, Board of Trustees of the University of Illinois
*
************************************************************************
*
* md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
*
* Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
* Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
* University (see Copyright below).
* Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
* Research, Inc. (Bellcore) (see Copyright below).
* Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
* Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
*
*/
/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
/* (C) Copyright 1993,1994 by Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Carnegie
* Mellon University not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. Carnegie Mellon University makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
* Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
*
* Permission to use, copy, modify, and distribute this material
* for any purpose and without fee is hereby granted, provided
* that the above copyright notice and this permission notice
* appear in all copies, and that the name of Bellcore not be
* used in advertising or publicity pertaining to this
* material without the specific, prior written permission
* of an authorized representative of Bellcore. BELLCORE
* MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
* OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
* WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
*/
For the srclib\apr\include\apr_md5.h component:
/*
* This is work is derived from material Copyright RSA Data Security, Inc.
*
* The RSA copyright statement and Licence for that original material is
* included below. This is followed by the Apache copyright statement and
* licence for the modifications made to that material.
*/
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
For the srclib\apr\passwd\apr_md5.c component:
/*
* This is work is derived from material Copyright RSA Data Security, Inc.
*
* The RSA copyright statement and Licence for that original material is
* included below. This is followed by the Apache copyright statement and
* licence for the modifications made to that material.
*/
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
*/
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.
License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.
License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.
RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
/*
* The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
* MD5 crypt() function, which is licenced as follows:
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/
For the srclib\apr-util\crypto\apr_md4.c component:
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
* rights reserved.
*
* License to copy and use this software is granted provided that it
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
* Algorithm" in all material mentioning or referencing this software
* or this function.
*
* License is also granted to make and use derivative works provided
* that such works are identified as "derived from the RSA Data
* Security, Inc. MD4 Message-Digest Algorithm" in all material
* mentioning or referencing the derived work.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\include\apr_md4.h component:
*
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
* rights reserved.
*
* License to copy and use this software is granted provided that it
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
* Algorithm" in all material mentioning or referencing this software
* or this function.
*
* License is also granted to make and use derivative works provided
* that such works are identified as "derived from the RSA Data
* Security, Inc. MD4 Message-Digest Algorithm" in all material
* mentioning or referencing the derived work.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\test\testdbm.c component:
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* This file came from the SDBM package (written by oz@nexus.yorku.ca).
* That package was under public domain. This file has been ported to
* APR, updated to ANSI C and other, newer idioms, and added to the Apache
* codebase under the above copyright and license.
*/
For the srclib\apr-util\test\testmd4.c component:
*
* This is derived from material copyright RSA Data Security, Inc.
* Their notice is reproduced below in its entirety.
*
* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
* rights reserved.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
For the srclib\apr-util\xml\expat\conftools\install-sh component:
#
# install - install a program, script, or datafile
# This comes from X11R5 (mit/util/scripts/install.sh).
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
For the srclib\pcre\install-sh component:
#
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
For the pcre component:
PCRE LICENCE
------------
PCRE is a library of functions to support regular expressions whose syntax
and semantics are as close as possible to those of the Perl 5 language.
Written by: Philip Hazel <ph10@cam.ac.uk>
University of Cambridge Computing Service,
Cambridge, England. Phone: +44 1223 334714.
Copyright (c) 1997-2001 University of Cambridge
Permission is granted to anyone to use this software for any purpose on any
computer system, and to redistribute it freely, subject to the following
restrictions:
1. This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2. The origin of this software must not be misrepresented, either by
explicit claim or by omission. In practice, this means that if you use
PCRE in software which you distribute to others, commercially or
otherwise, you must put a sentence like this
Regular expression support is provided by the PCRE library package,
which is open source software, written by Philip Hazel, and copyright
by the University of Cambridge, England.
somewhere reasonably visible in your documentation and in any relevant
files or online help data or similar. A reference to the ftp site for
the source, that is, to
ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
should also be given in the documentation.
3. Altered versions must be plainly marked as such, and must not be
misrepresented as being the original software.
4. If PCRE is embedded in any software that is released under the GNU
General Purpose Licence (GPL), or Lesser General Purpose Licence (LGPL),
then the terms of that licence shall supersede any condition above with
which it is incompatible.
The documentation for PCRE, supplied in the "doc" directory, is distributed
under the same terms as the software itself.
End PCRE LICENCE
For the test\zb.c component:
/* ZeusBench V1.01
===============
This program is Copyright (C) Zeus Technology Limited 1996.
This program may be used and copied freely providing this copyright notice
is not removed.
This software is provided "as is" and any express or implied waranties,
including but not limited to, the implied warranties of merchantability and
fitness for a particular purpose are disclaimed. In no event shall
Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
exemplary, or consequential damaged (including, but not limited to,
procurement of substitute good or services; loss of use, data, or profits;
or business interruption) however caused and on theory of liability. Whether
in contract, strict liability or tort (including negligence or otherwise)
arising in any way out of the use of this software, even if advised of the
possibility of such damage.
Written by Adam Twiss (adam@zeus.co.uk). March 1996
Thanks to the following people for their input:
Mike Belshe (mbelshe@netscape.com)
Michael Campanella (campanella@stevms.enet.dec.com)
*/
For the expat xml parser component:
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
and Clark Cooper
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
====================================================================

11
vendor/gopkg.in/ns1/ns1-go.v2/Makefile generated vendored Normal file
View File

@ -0,0 +1,11 @@
.PHONY: all clean
all: .git/hooks/pre-commit
go build .
clean:
rm -f terraform-provider-nsone
.git/hooks/pre-commit:
if [ ! -f .git/hooks/pre-commit ]; then ln -s ../../git-hooks/pre-commit .git/hooks/pre-commit; fi

62
vendor/gopkg.in/ns1/ns1-go.v2/README.md generated vendored Normal file
View File

@ -0,0 +1,62 @@
[![Build Status](https://travis-ci.org/ns1/ns1-go.svg?branch=v2)](https://travis-ci.org/ns1/ns1-go) [![GoDoc](https://godoc.org/gopkg.in/ns1/ns1-go.v2?status.svg)](https://godoc.org/gopkg.in/ns1/ns1-go.v2)
# NS1 Golang SDK
The golang client for the NS1 API: https://api.nsone.net/
# Installing
```
$ go get gopkg.in/ns1/ns1-go.v2
```
Examples
========
[See more](https://github.com/ns1/ns1-go/tree/v2/rest/_examples)
```go
package main
import (
"fmt"
"log"
"net/http"
"os"
"time"
api "gopkg.in/ns1/ns1-go.v2/rest"
)
func main() {
k := os.Getenv("NS1_APIKEY")
if k == "" {
fmt.Println("NS1_APIKEY environment variable is not set, giving up")
}
httpClient := &http.Client{Timeout: time.Second * 10}
client := api.NewClient(httpClient, api.SetAPIKey(k))
zones, _, err := client.Zones.List()
if err != nil {
log.Fatal(err)
}
for _, z := range zones {
fmt.Println(z.Zone)
}
}
```
Contributing
============
Contributions, ideas and criticisms are all welcome.
# LICENSE
Apache2 - see the included LICENSE file for more information

8
vendor/gopkg.in/ns1/ns1-go.v2/doc.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
// Package ns1 is the NS1 golang SDK.
//
// To understand the REST models and terminology,
// please visit the ns1 web page:
//
// https://ns1.com/
//
package ns1

143
vendor/gopkg.in/ns1/ns1-go.v2/rest/account_apikey.go generated vendored Normal file
View File

@ -0,0 +1,143 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
// APIKeysService handles 'account/apikeys' endpoint.
type APIKeysService service
// List returns all api keys in the account.
//
// NS1 API docs: https://ns1.com/api/#apikeys-get
func (s *APIKeysService) List() ([]*account.APIKey, *http.Response, error) {
req, err := s.client.NewRequest("GET", "account/apikeys", nil)
if err != nil {
return nil, nil, err
}
kl := []*account.APIKey{}
resp, err := s.client.Do(req, &kl)
if err != nil {
return nil, resp, err
}
return kl, resp, nil
}
// Get returns details of an api key, including permissions, for a single API Key.
// Note: do not use the API Key itself as the keyid in the URL — use the id of the key.
//
// NS1 API docs: https://ns1.com/api/#apikeys-id-get
func (s *APIKeysService) Get(keyID string) (*account.APIKey, *http.Response, error) {
path := fmt.Sprintf("account/apikeys/%s", keyID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var a account.APIKey
resp, err := s.client.Do(req, &a)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown api key" {
return nil, resp, ErrKeyMissing
}
default:
return nil, resp, err
}
}
return &a, resp, nil
}
// Create takes a *APIKey and creates a new account apikey.
//
// NS1 API docs: https://ns1.com/api/#apikeys-put
func (s *APIKeysService) Create(a *account.APIKey) (*http.Response, error) {
req, err := s.client.NewRequest("PUT", "account/apikeys", &a)
if err != nil {
return nil, err
}
// Update account fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &a)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == fmt.Sprintf("api key with name \"%s\" exists", a.Name) {
return resp, ErrKeyExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update changes the name or access rights for an API Key.
//
// NS1 API docs: https://ns1.com/api/#apikeys-id-post
func (s *APIKeysService) Update(a *account.APIKey) (*http.Response, error) {
path := fmt.Sprintf("account/apikeys/%s", a.ID)
req, err := s.client.NewRequest("POST", path, &a)
if err != nil {
return nil, err
}
// Update apikey fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &a)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown api key" {
return resp, ErrKeyMissing
}
default:
return resp, err
}
}
return resp, nil
}
// Delete deletes an apikey.
//
// NS1 API docs: https://ns1.com/api/#apikeys-id-delete
func (s *APIKeysService) Delete(keyID string) (*http.Response, error) {
path := fmt.Sprintf("account/apikeys/%s", keyID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown api key" {
return resp, ErrKeyMissing
}
default:
return resp, err
}
}
return resp, nil
}
var (
// ErrKeyExists bundles PUT create error.
ErrKeyExists = errors.New("Key already exists.")
// ErrKeyMissing bundles GET/POST/DELETE error.
ErrKeyMissing = errors.New("Key does not exist.")
)

46
vendor/gopkg.in/ns1/ns1-go.v2/rest/account_setting.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
package rest
import (
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
// SettingsService handles 'account/settings' endpoint.
type SettingsService service
// Get returns the basic contact details associated with the account.
//
// NS1 API docs: https://ns1.com/api/#settings-get
func (s *SettingsService) Get() (*account.Setting, *http.Response, error) {
req, err := s.client.NewRequest("GET", "account/settings", nil)
if err != nil {
return nil, nil, err
}
var us account.Setting
resp, err := s.client.Do(req, &us)
if err != nil {
return nil, resp, err
}
return &us, resp, nil
}
// Update changes most of the basic contact details, except customerid.
//
// NS1 API docs: https://ns1.com/api/#settings-post
func (s *SettingsService) Update(us *account.Setting) (*http.Response, error) {
req, err := s.client.NewRequest("POST", "account/settings", &us)
if err != nil {
return nil, err
}
// Update usagewarnings fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &us)
if err != nil {
return resp, err
}
return resp, nil
}

142
vendor/gopkg.in/ns1/ns1-go.v2/rest/account_team.go generated vendored Normal file
View File

@ -0,0 +1,142 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
// TeamsService handles 'account/teams' endpoint.
type TeamsService service
// List returns all teams in the account.
//
// NS1 API docs: https://ns1.com/api/#teams-get
func (s *TeamsService) List() ([]*account.Team, *http.Response, error) {
req, err := s.client.NewRequest("GET", "account/teams", nil)
if err != nil {
return nil, nil, err
}
tl := []*account.Team{}
resp, err := s.client.Do(req, &tl)
if err != nil {
return nil, resp, err
}
return tl, resp, nil
}
// Get returns details of a single team.
//
// NS1 API docs: https://ns1.com/api/#teams-id-get
func (s *TeamsService) Get(id string) (*account.Team, *http.Response, error) {
path := fmt.Sprintf("account/teams/%s", id)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var t account.Team
resp, err := s.client.Do(req, &t)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "Unknown team id" {
return nil, resp, ErrTeamMissing
}
default:
return nil, resp, err
}
}
return &t, resp, nil
}
// Create takes a *Team and creates a new account team.
//
// NS1 API docs: https://ns1.com/api/#teams-put
func (s *TeamsService) Create(t *account.Team) (*http.Response, error) {
req, err := s.client.NewRequest("PUT", "account/teams", &t)
if err != nil {
return nil, err
}
// Update team fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &t)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == fmt.Sprintf("team with name \"%s\" exists", t.Name) {
return resp, ErrTeamExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update changes the name or access rights for a team.
//
// NS1 API docs: https://ns1.com/api/#teams-id-post
func (s *TeamsService) Update(t *account.Team) (*http.Response, error) {
path := fmt.Sprintf("account/teams/%s", t.ID)
req, err := s.client.NewRequest("POST", path, &t)
if err != nil {
return nil, err
}
// Update team fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &t)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown team id" {
return resp, ErrTeamMissing
}
default:
return resp, err
}
}
return resp, nil
}
// Delete deletes a team.
//
// NS1 API docs: https://ns1.com/api/#teams-id-delete
func (s *TeamsService) Delete(id string) (*http.Response, error) {
path := fmt.Sprintf("account/teams/%s", id)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown team id" {
return resp, ErrTeamMissing
}
default:
return resp, err
}
}
return resp, nil
}
var (
// ErrTeamExists bundles PUT create error.
ErrTeamExists = errors.New("Team already exists.")
// ErrTeamMissing bundles GET/POST/DELETE error.
ErrTeamMissing = errors.New("Team does not exist.")
)

142
vendor/gopkg.in/ns1/ns1-go.v2/rest/account_user.go generated vendored Normal file
View File

@ -0,0 +1,142 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
// UsersService handles 'account/users' endpoint.
type UsersService service
// List returns all users in the account.
//
// NS1 API docs: https://ns1.com/api/#users-get
func (s *UsersService) List() ([]*account.User, *http.Response, error) {
req, err := s.client.NewRequest("GET", "account/users", nil)
if err != nil {
return nil, nil, err
}
ul := []*account.User{}
resp, err := s.client.Do(req, &ul)
if err != nil {
return nil, resp, err
}
return ul, resp, nil
}
// Get returns details of a single user.
//
// NS1 API docs: https://ns1.com/api/#users-user-get
func (s *UsersService) Get(username string) (*account.User, *http.Response, error) {
path := fmt.Sprintf("account/users/%s", username)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var u account.User
resp, err := s.client.Do(req, &u)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "Unknown user" {
return nil, resp, ErrUserMissing
}
default:
return nil, resp, err
}
}
return &u, resp, nil
}
// Create takes a *User and creates a new account user.
//
// NS1 API docs: https://ns1.com/api/#users-put
func (s *UsersService) Create(u *account.User) (*http.Response, error) {
req, err := s.client.NewRequest("PUT", "account/users", &u)
if err != nil {
return nil, err
}
// Update user fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &u)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "request failed:Login Name is already in use." {
return resp, ErrUserExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update change contact details, notification settings, or access rights for a user.
//
// NS1 API docs: https://ns1.com/api/#users-user-post
func (s *UsersService) Update(u *account.User) (*http.Response, error) {
path := fmt.Sprintf("account/users/%s", u.Username)
req, err := s.client.NewRequest("POST", path, &u)
if err != nil {
return nil, err
}
// Update user fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &u)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "Unknown user" {
return resp, ErrUserMissing
}
default:
return resp, err
}
}
return resp, nil
}
// Delete deletes a user.
//
// NS1 API docs: https://ns1.com/api/#users-user-delete
func (s *UsersService) Delete(username string) (*http.Response, error) {
path := fmt.Sprintf("account/users/%s", username)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "Unknown user" {
return resp, ErrUserMissing
}
default:
return resp, err
}
}
return resp, nil
}
var (
// ErrUserExists bundles PUT create error.
ErrUserExists = errors.New("User already exists.")
// ErrUserMissing bundles GET/POST/DELETE error.
ErrUserMissing = errors.New("User does not exist.")
)

47
vendor/gopkg.in/ns1/ns1-go.v2/rest/account_warning.go generated vendored Normal file
View File

@ -0,0 +1,47 @@
package rest
import (
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/account"
)
// WarningsService handles 'account/usagewarnings' endpoint.
type WarningsService service
// Get returns toggles and thresholds used when sending overage warning
// alert messages to users with billing notifications enabled.
//
// NS1 API docs: https://ns1.com/api/#usagewarnings-get
func (s *WarningsService) Get() (*account.UsageWarning, *http.Response, error) {
req, err := s.client.NewRequest("GET", "account/usagewarnings", nil)
if err != nil {
return nil, nil, err
}
var uw account.UsageWarning
resp, err := s.client.Do(req, &uw)
if err != nil {
return nil, resp, err
}
return &uw, resp, nil
}
// Update changes alerting toggles and thresholds for overage warning alert messages.
//
// NS1 API docs: https://ns1.com/api/#usagewarnings-post
func (s *WarningsService) Update(uw *account.UsageWarning) (*http.Response, error) {
req, err := s.client.NewRequest("POST", "account/usagewarnings", &uw)
if err != nil {
return nil, err
}
// Update usagewarnings fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &uw)
if err != nil {
return resp, err
}
return resp, nil
}

273
vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go generated vendored Normal file
View File

@ -0,0 +1,273 @@
package rest
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"time"
)
const (
clientVersion = "2.0.0"
defaultEndpoint = "https://api.nsone.net/v1/"
defaultUserAgent = "go-ns1/" + clientVersion
headerAuth = "X-NSONE-Key"
headerRateLimit = "X-Ratelimit-Limit"
headerRateRemaining = "X-Ratelimit-Remaining"
headerRatePeriod = "X-Ratelimit-Period"
)
// Doer is a single method interface that allows a user to extend/augment an http.Client instance.
// Note: http.Client satisfies the Doer interface.
type Doer interface {
Do(*http.Request) (*http.Response, error)
}
// Client manages communication with the NS1 Rest API.
type Client struct {
// httpClient handles all rest api communication,
// and expects an *http.Client.
httpClient Doer
// NS1 rest endpoint, overrides default if given.
Endpoint *url.URL
// NS1 api key (value for http request header 'X-NSONE-Key').
APIKey string
// NS1 go rest user agent (value for http request header 'User-Agent').
UserAgent string
// Func to call after response is returned in Do
RateLimitFunc func(RateLimit)
// From the excellent github-go client.
common service // Reuse a single struct instead of allocating one for each service on the heap.
// Services used for communicating with different components of the NS1 API.
APIKeys *APIKeysService
DataFeeds *DataFeedsService
DataSources *DataSourcesService
Jobs *JobsService
Notifications *NotificationsService
Records *RecordsService
Settings *SettingsService
Teams *TeamsService
Users *UsersService
Warnings *WarningsService
Zones *ZonesService
}
// NewClient constructs and returns a reference to an instantiated Client.
func NewClient(httpClient Doer, options ...func(*Client)) *Client {
endpoint, _ := url.Parse(defaultEndpoint)
if httpClient == nil {
httpClient = http.DefaultClient
}
c := &Client{
httpClient: httpClient,
Endpoint: endpoint,
RateLimitFunc: defaultRateLimitFunc,
UserAgent: defaultUserAgent,
}
c.common.client = c
c.APIKeys = (*APIKeysService)(&c.common)
c.DataFeeds = (*DataFeedsService)(&c.common)
c.DataSources = (*DataSourcesService)(&c.common)
c.Jobs = (*JobsService)(&c.common)
c.Notifications = (*NotificationsService)(&c.common)
c.Records = (*RecordsService)(&c.common)
c.Settings = (*SettingsService)(&c.common)
c.Teams = (*TeamsService)(&c.common)
c.Users = (*UsersService)(&c.common)
c.Warnings = (*WarningsService)(&c.common)
c.Zones = (*ZonesService)(&c.common)
for _, option := range options {
option(c)
}
return c
}
type service struct {
client *Client
}
// SetHTTPClient sets a Client instances' httpClient.
func SetHTTPClient(httpClient Doer) func(*Client) {
return func(c *Client) { c.httpClient = httpClient }
}
// SetAPIKey sets a Client instances' APIKey.
func SetAPIKey(key string) func(*Client) {
return func(c *Client) { c.APIKey = key }
}
// SetEndpoint sets a Client instances' Endpoint.
func SetEndpoint(endpoint string) func(*Client) {
return func(c *Client) { c.Endpoint, _ = url.Parse(endpoint) }
}
// SetUserAgent sets a Client instances' user agent.
func SetUserAgent(ua string) func(*Client) {
return func(c *Client) { c.UserAgent = ua }
}
// SetRateLimitFunc sets a Client instances' RateLimitFunc.
func SetRateLimitFunc(ratefunc func(rl RateLimit)) func(*Client) {
return func(c *Client) { c.RateLimitFunc = ratefunc }
}
// Do satisfies the Doer interface.
func (c Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
err = CheckResponse(resp)
if err != nil {
return resp, err
}
rl := parseRate(resp)
c.RateLimitFunc(rl)
if v != nil {
// Try to unmarshal body into given type using streaming decoder.
if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
return nil, err
}
}
return resp, err
}
// NewRequest constructs and returns a http.Request.
func (c *Client) NewRequest(method, path string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(path)
if err != nil {
return nil, err
}
uri := c.Endpoint.ResolveReference(rel)
// Encode body as json
buf := new(bytes.Buffer)
if body != nil {
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, uri.String(), buf)
if err != nil {
return nil, err
}
req.Header.Add(headerAuth, c.APIKey)
req.Header.Add("User-Agent", c.UserAgent)
return req, nil
}
// Response wraps stdlib http response.
type Response struct {
*http.Response
}
// Error contains all http responses outside the 2xx range.
type Error struct {
Resp *http.Response
Message string
}
// Satisfy std lib error interface.
func (re *Error) Error() string {
return fmt.Sprintf("%v %v: %d %v", re.Resp.Request.Method, re.Resp.Request.URL, re.Resp.StatusCode, re.Message)
}
// CheckResponse handles parsing of rest api errors. Returns nil if no error.
func CheckResponse(resp *http.Response) error {
if c := resp.StatusCode; c >= 200 && c <= 299 {
return nil
}
restErr := &Error{Resp: resp}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if len(b) == 0 {
return restErr
}
err = json.Unmarshal(b, restErr)
if err != nil {
return err
}
return restErr
}
// RateLimitFunc is rate limiting strategy for the Client instance.
type RateLimitFunc func(RateLimit)
// RateLimit stores X-Ratelimit-* headers
type RateLimit struct {
Limit int
Remaining int
Period int
}
var defaultRateLimitFunc = func(rl RateLimit) {}
// PercentageLeft returns the ratio of Remaining to Limit as a percentage
func (rl RateLimit) PercentageLeft() int {
return rl.Remaining * 100 / rl.Limit
}
// WaitTime returns the time.Duration ratio of Period to Limit
func (rl RateLimit) WaitTime() time.Duration {
return (time.Second * time.Duration(rl.Period)) / time.Duration(rl.Limit)
}
// WaitTimeRemaining returns the time.Duration ratio of Period to Remaining
func (rl RateLimit) WaitTimeRemaining() time.Duration {
return (time.Second * time.Duration(rl.Period)) / time.Duration(rl.Remaining)
}
// RateLimitStrategySleep sets RateLimitFunc to sleep by WaitTimeRemaining
func (c *Client) RateLimitStrategySleep() {
c.RateLimitFunc = func(rl RateLimit) {
remaining := rl.WaitTimeRemaining()
time.Sleep(remaining)
}
}
// parseRate parses rate related headers from http response.
func parseRate(resp *http.Response) RateLimit {
var rl RateLimit
if limit := resp.Header.Get(headerRateLimit); limit != "" {
rl.Limit, _ = strconv.Atoi(limit)
}
if remaining := resp.Header.Get(headerRateRemaining); remaining != "" {
rl.Remaining, _ = strconv.Atoi(remaining)
}
if period := resp.Header.Get(headerRatePeriod); period != "" {
rl.Period, _ = strconv.Atoi(period)
}
return rl
}

30
vendor/gopkg.in/ns1/ns1-go.v2/rest/client_test.go generated vendored Normal file
View File

@ -0,0 +1,30 @@
package rest
import (
"testing"
"time"
)
func TestRateLimit(t *testing.T) {
r := RateLimit{
Limit: 10,
Remaining: 10,
Period: 10,
}
if r.WaitTime() != time.Second {
t.Error("WaitTime is wrong duration ", r.WaitTime())
}
if r.PercentageLeft() != 100 {
t.Error("PercentLeft != 100")
}
r.Remaining = 5
if r.PercentageLeft() != 50 {
t.Error("PercentLeft != 50")
}
if r.WaitTime() != time.Second {
t.Error("WaitTime is wrong duration ", r.WaitTime())
}
if r.WaitTimeRemaining() != (time.Duration(2) * time.Second) {
t.Error("WaitTimeRemaining is wrong duration ", r.WaitTimeRemaining())
}
}

116
vendor/gopkg.in/ns1/ns1-go.v2/rest/data_feed.go generated vendored Normal file
View File

@ -0,0 +1,116 @@
package rest
import (
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
// DataFeedsService handles 'data/feeds' endpoint.
type DataFeedsService service
// List returns all data feeds connected to a given data source.
//
// NS1 API docs: https://ns1.com/api/#feeds-get
func (s *DataFeedsService) List(sourceID string) ([]*data.Feed, *http.Response, error) {
path := fmt.Sprintf("data/feeds/%s", sourceID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
dfl := []*data.Feed{}
resp, err := s.client.Do(req, &dfl)
if err != nil {
return nil, resp, err
}
return dfl, resp, nil
}
// Get takes a data source ID and a data feed ID and returns the details of a single data feed
//
// NS1 API docs: https://ns1.com/api/#feeds-feed-get
func (s *DataFeedsService) Get(sourceID string, feedID string) (*data.Feed, *http.Response, error) {
path := fmt.Sprintf("data/feeds/%s/%s", sourceID, feedID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var df data.Feed
resp, err := s.client.Do(req, &df)
if err != nil {
return nil, resp, err
}
return &df, resp, nil
}
// Create takes a *DataFeed and connects a new data feed to an existing data source.
//
// NS1 API docs: https://ns1.com/api/#feeds-put
func (s *DataFeedsService) Create(sourceID string, df *data.Feed) (*http.Response, error) {
path := fmt.Sprintf("data/feeds/%s", sourceID)
req, err := s.client.NewRequest("PUT", path, &df)
if err != nil {
return nil, err
}
// Update datafeeds' fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &df)
if err != nil {
return resp, err
}
return resp, nil
}
// Update takes a *Feed and modifies and existing data feed.
// Note:
// - The 'data' portion of a feed does not actually
// get updated during a POST. In order to update a feeds'
// 'data' attribute, one must use the Publish method.
// - Both the 'destinations' and 'networks' attributes are
// not updated during a POST.
//
// NS1 API docs: https://ns1.com/api/#feeds-post
func (s *DataFeedsService) Update(sourceID string, df *data.Feed) (*http.Response, error) {
path := fmt.Sprintf("data/feeds/%s/%s", sourceID, df.ID)
req, err := s.client.NewRequest("POST", path, &df)
if err != nil {
return nil, err
}
// Update df instance fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &df)
if err != nil {
return resp, err
}
return resp, nil
}
// Delete takes a data source ID and a data feed ID and disconnects the feed from the data source and all attached destination metadata tables.
//
// NS1 API docs: https://ns1.com/api/#feeds-delete
func (s *DataFeedsService) Delete(sourceID string, feedID string) (*http.Response, error) {
path := fmt.Sprintf("data/feeds/%s/%s", sourceID, feedID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

126
vendor/gopkg.in/ns1/ns1-go.v2/rest/data_source.go generated vendored Normal file
View File

@ -0,0 +1,126 @@
package rest
import (
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
// DataSourcesService handles 'data/sources' endpoint.
type DataSourcesService service
// List returns all connected data sources.
//
// NS1 API docs: https://ns1.com/api/#sources-get
func (s *DataSourcesService) List() ([]*data.Source, *http.Response, error) {
req, err := s.client.NewRequest("GET", "data/sources", nil)
if err != nil {
return nil, nil, err
}
dsl := []*data.Source{}
resp, err := s.client.Do(req, &dsl)
if err != nil {
return nil, resp, err
}
return dsl, resp, nil
}
// Get takes an ID returns the details for a single data source.
//
// NS1 API docs: https://ns1.com/api/#sources-source-get
func (s *DataSourcesService) Get(id string) (*data.Source, *http.Response, error) {
path := fmt.Sprintf("data/sources/%s", id)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var ds data.Source
resp, err := s.client.Do(req, &ds)
if err != nil {
return nil, resp, err
}
return &ds, resp, nil
}
// Create takes a *DataSource and creates a new data source.
//
// NS1 API docs: https://ns1.com/api/#sources-put
func (s *DataSourcesService) Create(ds *data.Source) (*http.Response, error) {
req, err := s.client.NewRequest("PUT", "data/sources", &ds)
if err != nil {
return nil, err
}
// Update data sources' fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &ds)
if err != nil {
return resp, err
}
return resp, nil
}
// Update takes a *DataSource modifies basic details of a data source.
// NOTE: This does not 'publish' data. See the Publish method.
//
// NS1 API docs: https://ns1.com/api/#sources-post
func (s *DataSourcesService) Update(ds *data.Source) (*http.Response, error) {
path := fmt.Sprintf("data/sources/%s", ds.ID)
req, err := s.client.NewRequest("POST", path, &ds)
if err != nil {
return nil, err
}
// Update data sources' instance fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &ds)
if err != nil {
return resp, err
}
return resp, nil
}
// Delete takes an ID and removes an existing data source and all connected feeds from the source.
//
// NS1 API docs: https://ns1.com/api/#sources-delete
func (s *DataSourcesService) Delete(id string) (*http.Response, error) {
path := fmt.Sprintf("data/sources/%s", id)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, err
}
return resp, nil
}
// Publish takes a datasources' id and data to publish.
//
// NS1 API docs: https://ns1.com/api/#feed-post
func (s *DataSourcesService) Publish(dsID string, data interface{}) (*http.Response, error) {
path := fmt.Sprintf("feed/%s", dsID)
req, err := s.client.NewRequest("POST", path, &data)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

2
vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package rest defines the api services used to communicate with NS1.
package rest

View File

@ -0,0 +1,13 @@
package account
// APIKey wraps an NS1 /account/apikeys resource
type APIKey struct {
// Read-only fields
ID string `json:"id,omitempty"`
Key string `json:"key,omitempty"`
LastAccess int `json:"last_access,omitempty"`
Name string `json:"name"`
TeamIDs []string `json:"teams"`
Permissions PermissionsMap `json:"permissions"`
}

View File

@ -0,0 +1,2 @@
// Package account contains definitions for NS1 apikeys/teams/users/etc.
package account

View File

@ -0,0 +1,44 @@
package account
// PermissionsMap wraps a User's "permissions" attribute
type PermissionsMap struct {
DNS PermissionsDNS `json:"dns"`
Data PermissionsData `json:"data"`
Account PermissionsAccount `json:"account"`
Monitoring PermissionsMonitoring `json:"monitoring"`
}
// PermissionsDNS wraps a User's "permissions.dns" attribute
type PermissionsDNS struct {
ViewZones bool `json:"view_zones"`
ManageZones bool `json:"manage_zones"`
ZonesAllowByDefault bool `json:"zones_allow_by_default"`
ZonesDeny []string `json:"zones_deny"`
ZonesAllow []string `json:"zones_allow"`
}
// PermissionsData wraps a User's "permissions.data" attribute
type PermissionsData struct {
PushToDatafeeds bool `json:"push_to_datafeeds"`
ManageDatasources bool `json:"manage_datasources"`
ManageDatafeeds bool `json:"manage_datafeeds"`
}
// PermissionsAccount wraps a User's "permissions.account" attribute
type PermissionsAccount struct {
ManageUsers bool `json:"manage_users"`
ManagePaymentMethods bool `json:"manage_payment_methods"`
ManagePlan bool `json:"manage_plan"`
ManageTeams bool `json:"manage_teams"`
ManageApikeys bool `json:"manage_apikeys"`
ManageAccountSettings bool `json:"manage_account_settings"`
ViewActivityLog bool `json:"view_activity_log"`
ViewInvoices bool `json:"view_invoices"`
}
// PermissionsMonitoring wraps a User's "permissions.monitoring" attribute
type PermissionsMonitoring struct {
ManageLists bool `json:"manage_lists"`
ManageJobs bool `json:"manage_jobs"`
ViewJobs bool `json:"view_jobs"`
}

View File

@ -0,0 +1,21 @@
package account
// Setting represents an accounts' contact info.
type Setting struct {
CustomerID int `json:"customerid,omitempty"`
FirstName string `json:"firstname,omitempty"`
LastName string `json:"lastname,omitempty"`
Company string `json:"company,omitempty"`
Phone string `json:"phone,omitempty"`
Email string `json:"email,omitempty"`
Address Address `json:"address,omitempty"`
}
// Address for Setting struct.
type Address struct {
Country string `json:"country,omitempty"`
Street string `json:"street,omitempty"`
State string `json:"state,omitempty"`
City string `json:"city,omitempty"`
Postal string `json:"postalcode,omitempty"`
}

View File

@ -0,0 +1,8 @@
package account
// Team wraps an NS1 /accounts/teams resource
type Team struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Permissions PermissionsMap `json:"permissions"`
}

View File

@ -0,0 +1,19 @@
package account
// User wraps an NS1 /account/users resource
type User struct {
// Read-only fields
LastAccess float64 `json:"last_access"`
Name string `json:"name"`
Username string `json:"username"`
Email string `json:"email"`
TeamIDs []string `json:"teams"`
Notify NotificationSettings `json:"notify"`
Permissions PermissionsMap `json:"permissions"`
}
// NotificationSettings wraps a User's "notify" attribute
type NotificationSettings struct {
Billing bool `json:"billing"`
}

View File

@ -0,0 +1,117 @@
package account
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnmarshalUsers(t *testing.T) {
d := []byte(`[
{
"permissions": {},
"teams": [],
"email": "support@nsone.net",
"last_access": 1376325771.0,
"notify": {
"billing": true
},
"name": "API Example",
"username": "apiexample"
},
{
"permissions": {
"dns": {
"view_zones": true,
"manage_zones": true,
"zones_allow_by_default": false,
"zones_deny": [],
"zones_allow": ["example.com"]
},
"data": {
"push_to_datafeeds": false,
"manage_datasources": false,
"manage_datafeeds": false
},
"account": {
"manage_payment_methods": false,
"manage_plan": false,
"manage_teams": false,
"manage_apikeys": false,
"manage_account_settings": false,
"view_activity_log": false,
"view_invoices": false,
"manage_users": false
},
"monitoring": {
"manage_lists": false,
"manage_jobs": false,
"view_jobs": false
}
},
"teams": ["520422919f782d37dffb588a"],
"email": "newuser@example.com",
"last_access": null,
"notify": {
"billing": true
},
"name": "New User",
"username": "newuser"
}
]
`)
ul := []*User{}
if err := json.Unmarshal(d, &ul); err != nil {
t.Error(err)
}
assert.Equal(t, len(ul), 2, "Userlist should have 2 users")
u := ul[0]
assert.Equal(t, u.TeamIDs, []string{}, "User should have empty teams")
assert.Equal(t, u.Email, "support@nsone.net", "User wrong email")
assert.Equal(t, u.LastAccess, 1376325771.0, "User wrong last access")
assert.Equal(t, u.Name, "API Example", "User wrong name")
assert.Equal(t, u.Username, "apiexample", "User wrong username")
assert.Equal(t, u.Notify, NotificationSettings{true}, "User wrong notify")
assert.Equal(t, u.Permissions, PermissionsMap{}, "User should have empty permissions")
u2 := ul[1]
assert.Equal(t, u2.TeamIDs, []string{"520422919f782d37dffb588a"}, "User should have empty teams")
assert.Equal(t, u2.Email, "newuser@example.com", "User wrong email")
assert.Equal(t, u2.LastAccess, 0.0, "User wrong last access")
assert.Equal(t, u2.Name, "New User", "User wrong name")
assert.Equal(t, u2.Username, "newuser", "User wrong username")
assert.Equal(t, u.Notify, NotificationSettings{true}, "User wrong notify")
permMap := PermissionsMap{
DNS: PermissionsDNS{
ViewZones: true,
ManageZones: true,
ZonesAllowByDefault: false,
ZonesDeny: []string{},
ZonesAllow: []string{"example.com"},
},
Data: PermissionsData{
PushToDatafeeds: false,
ManageDatasources: false,
ManageDatafeeds: false,
},
Account: PermissionsAccount{
ManagePaymentMethods: false,
ManagePlan: false,
ManageTeams: false,
ManageApikeys: false,
ManageAccountSettings: false,
ViewActivityLog: false,
ViewInvoices: false,
ManageUsers: false,
},
Monitoring: PermissionsMonitoring{
ManageLists: false,
ManageJobs: false,
ViewJobs: false,
},
}
assert.Equal(t, u2.Permissions, permMap, "User wrong permissions")
}

View File

@ -0,0 +1,17 @@
package account
// UsageWarning wraps an NS1 /account/usagewarnings resource
type UsageWarning struct {
Records Warning `json:"records"`
Queries Warning `json:"queries"`
}
// Warning contains alerting toggles and thresholds for overage warning alert messages.
// First thresholds must be smaller than Second ones and all thresholds
// must be percentages between 0 and 100.
type Warning struct {
Send bool `json:"send_warnings"`
First int `json:"warning_1"`
Second int `json:"warning_2"`
}

2
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package data contains definitions for NS1 metadata/sources/feeds/etc.
package data

View File

@ -0,0 +1,70 @@
package data_test
import (
"fmt"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
func ExampleSource() {
// Construct an NSONE API data source.
source := data.NewSource("my api source", "nsone_v1")
fmt.Println(source.ID) // will be empty string
fmt.Println(source.Name)
fmt.Println(source.Type)
// Output:
// my api source
// nsone_v1
}
func ExampleFeed() {
// Construct the london data feed.
feed := data.NewFeed(
"London Feed",
data.Config{"label": "London-UK"})
fmt.Println(feed.ID) // will be empty string
fmt.Println(feed.Name)
fmt.Println(feed.Config)
// Output:
// London Feed
// map[label:London-UK]
}
func ExampleMeta() {
feedID := "feed_id"
meta := data.Meta{}
meta.Priority = 1
meta.Up = data.FeedPtr{FeedID: feedID}
fmt.Println(meta.Connections) // will be nil
fmt.Println(meta.Priority)
fmt.Println(meta.Up)
// Output:
// <nil>
// 1
// {feed_id}
}
func ExampleRegions() {
feedPtr := data.FeedPtr{FeedID: "feed_id"}
regions := data.Regions{}
// Set a regions' 'up' metavalue to false('down').
regions["some_region"] = data.Region{
Meta: data.Meta{Up: false},
}
// Set a regions' 'connections' metavalue to receive from a feed.
regions["other_region"] = data.Region{
Meta: data.Meta{Connections: feedPtr},
}
fmt.Println(regions["some_region"].Meta.Up)
fmt.Println(regions["some_region"].Meta.Priority)
fmt.Println(regions["other_region"].Meta.Connections)
fmt.Println(regions["other_region"].Meta.Priority)
// Output:
// false
// <nil>
// {feed_id}
// <nil>
}

38
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/feed.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
package data
// Destination is the target resource the receives data from a feed/source.
type Destination struct {
ID string `json:"destid"`
// All destinations must point to a record.
RecordID string `json:"record"`
// Type is the 'level' at which to apply the filters(on the targeted record).
// Options:
// - answer (highest precedence)
// - region
// - record (lowest precendence)
Type string `json:"desttype"`
SourceID string `json:"-"`
}
// NewDestination returns an empty feed destination.
func NewDestination() *Destination {
return &Destination{}
}
// Feed wraps an NS1 /data/feeds resource
type Feed struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Config Config `json:"config,omitempty"`
Data Meta `json:"data,omitempty"`
SourceID string
}
// NewFeed returns a data feed with given name and config.
func NewFeed(name string, cfg Config) *Feed {
return &Feed{Name: name, Config: cfg}
}

127
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/meta.go generated vendored Normal file
View File

@ -0,0 +1,127 @@
package data
// FeedPtr represents the dynamic metadata value in which a feed is providing the value.
type FeedPtr struct {
FeedID string `json:"feed,omitempty"`
}
// Meta contains information on an entities metadata table. Metadata key/value
// pairs are used by a records' filter pipeline during a dns query.
// All values can be a feed id as well, indicating real-time updates of these values.
// Structure/Precendence of metadata tables:
// - Record
// - Meta <- lowest precendence in filter
// - Region(s)
// - Meta <- middle precedence in filter chain
// - ...
// - Answer(s)
// - Meta <- highest precedence in filter chain
// - ...
// - ...
type Meta struct {
// STATUS
// Indicates whether or not entity is considered 'up'
// bool or FeedPtr.
Up interface{} `json:"up,omitempty"`
// Indicates the number of active connections.
// Values must be positive.
// int or FeedPtr.
Connections interface{} `json:"connections,omitempty"`
// Indicates the number of active requests (HTTP or otherwise).
// Values must be positive.
// int or FeedPtr.
Requests interface{} `json:"requests,omitempty"`
// Indicates the "load average".
// Values must be positive, and will be rounded to the nearest tenth.
// float64 or FeedPtr.
LoadAvg interface{} `json:",loadavg,omitempty"`
// The Job ID of a Pulsar telemetry gathering job and routing granularities
// to associate with.
// string or FeedPtr.
Pulsar interface{} `json:"pulsar,omitempty"`
// GEOGRAPHICAL
// Must be between -180.0 and +180.0 where negative
// indicates South and positive indicates North.
// e.g., the longitude of the datacenter where a server resides.
// float64 or FeedPtr.
Latitude interface{} `json:"latitude,omitempty"`
// Must be between -180.0 and +180.0 where negative
// indicates West and positive indicates East.
// e.g., the longitude of the datacenter where a server resides.
// float64 or FeedPtr.
Longitude interface{} `json:"longitude,omitempty"`
// Valid geographic regions are: 'US-EAST', 'US-CENTRAL', 'US-WEST',
// 'EUROPE', 'ASIAPAC', 'SOUTH-AMERICA', 'AFRICA'.
// e.g., the rough geographic location of the Datacenter where a server resides.
// []string or FeedPtr.
Georegion interface{} `json:"georegion,omitempty"`
// Countr(ies) must be specified as ISO3166 2-character country code(s).
// []string or FeedPtr.
Country interface{} `json:"country,omitempty"`
// State(s) must be specified as standard 2-character state code(s).
// []string or FeedPtr.
USState interface{} `json:"us_state,omitempty"`
// Canadian Province(s) must be specified as standard 2-character province
// code(s).
// []string or FeedPtr.
CAProvince interface{} `json:"ca_province,omitempty"`
// INFORMATIONAL
// Notes to indicate any necessary details for operators.
// Up to 256 characters in length.
// string or FeedPtr.
Note interface{} `json:"note,omitempty"`
// NETWORK
// IP (v4 and v6) prefixes in CIDR format ("a.b.c.d/mask").
// May include up to 1000 prefixes.
// e.g., "1.2.3.4/24"
// []string or FeedPtr.
IPPrefixes interface{} `json:"ip_prefixes,omitempty"`
// Autonomous System (AS) number(s).
// May include up to 1000 AS numbers.
// []string or FeedPtr.
ASN interface{} `json:"asn,omitempty"`
// TRAFFIC
// Indicates the "priority tier".
// Lower values indicate higher priority.
// Values must be positive.
// int or FeedPtr.
Priority interface{} `json:"priority,omitempty"`
// Indicates a weight.
// Filters that use weights normalize them.
// Any positive values are allowed.
// Values between 0 and 100 are recommended for simplicity's sake.
// float64 or FeedPtr.
Weight interface{} `json:"weight,omitempty"`
// Indicates a "low watermark" to use for load shedding.
// The value should depend on the metric used to determine
// load (e.g., loadavg, connections, etc).
// int or FeedPtr.
LowWatermark interface{} `json:"low_watermark,omitempty"`
// Indicates a "high watermark" to use for load shedding.
// The value should depend on the metric used to determine
// load (e.g., loadavg, connections, etc).
// int or FeedPtr.
HighWatermark interface{} `json:"high_watermark,omitempty"`
}

View File

@ -0,0 +1,10 @@
package data
// Region is a metadata table with a name/key.
// Can be thought of as metadata groupings.
type Region struct {
Meta Meta `json:"meta,omitempty"`
}
// Regions is simply a mapping of Regions inside a record.
type Regions map[string]Region

View File

@ -0,0 +1,28 @@
package data
// Config is a flat mapping where values are simple (no slices/maps).
type Config map[string]interface{}
// Source wraps an NS1 /data/sources resource
type Source struct {
ID string `json:"id,omitempty"`
// Human readable name of the source.
Name string `json:"name"`
Type string `json:"sourcetype"`
Config Config `json:"config,omitempty"`
Status string `json:"status,omitempty"`
Feeds []*Feed `json:"feeds,omitempty"`
}
// NewSource takes a name and type t.
func NewSource(name string, t string) *Source {
return &Source{
Name: name,
Type: t,
Config: Config{},
Feeds: []*Feed{},
}
}

101
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/answer.go generated vendored Normal file
View File

@ -0,0 +1,101 @@
package dns
import (
"fmt"
"strconv"
"strings"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
)
// Answer wraps the values of a Record's "filters" attribute
type Answer struct {
Meta *data.Meta `json:"meta,omitempty"`
// Answer response data. eg:
// Av4: ["1.1.1.1"]
// Av6: ["2001:db8:85a3::8a2e:370:7334"]
// MX: [10, "2.2.2.2"]
Rdata []string `json:"answer"`
// Region(grouping) that answer belongs to.
RegionName string `json:"region,omitempty"`
}
func (a Answer) String() string {
return strings.Trim(fmt.Sprint(a.Rdata), "[]")
}
// SetRegion associates a region with this answer.
func (a *Answer) SetRegion(name string) {
a.RegionName = name
}
// NewAnswer creates a generic Answer with given rdata.
func NewAnswer(rdata []string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: rdata,
}
}
// NewAv4Answer creates an Answer for A record.
func NewAv4Answer(host string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{host},
}
}
// NewAv6Answer creates an Answer for AAAA record.
func NewAv6Answer(host string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{host},
}
}
// NewALIASAnswer creates an Answer for ALIAS record.
func NewALIASAnswer(host string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{host},
}
}
// NewCNAMEAnswer creates an Answer for CNAME record.
func NewCNAMEAnswer(name string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{name},
}
}
// NewTXTAnswer creates an Answer for TXT record.
func NewTXTAnswer(text string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{text},
}
}
// NewMXAnswer creates an Answer for MX record.
func NewMXAnswer(pri int, host string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{strconv.Itoa(pri), host},
}
}
// NewSRVAnswer creates an Answer for SRV record.
func NewSRVAnswer(priority, weight, port int, target string) *Answer {
return &Answer{
Meta: &data.Meta{},
Rdata: []string{
strconv.Itoa(priority),
strconv.Itoa(weight),
strconv.Itoa(port),
target,
},
}
}

2
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package dns contains definitions for NS1 zones/records/answers/etc.
package dns

View File

@ -0,0 +1,141 @@
package dns_test
import (
"encoding/json"
"fmt"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
"gopkg.in/ns1/ns1-go.v2/rest/model/filter"
)
func ExampleZone() {
z := dns.NewZone("example.com")
fmt.Println(z)
// Output:
// example.com
}
// Example references https://ns1.com/articles/primary-dns-with-ns1
func ExamplePrimaryZone() {
// Secondary/slave dns server info.
secondary := dns.ZoneSecondaryServer{
IP: "1.2.3.4",
Port: 53,
Notify: true,
}
// Construct the primary/master zone.
domain := "masterzone.example"
masterZone := dns.NewZone(domain)
masterZone.MakePrimary(secondary)
b, _ := json.MarshalIndent(masterZone, "", " ")
fmt.Println(string(b))
// Output:
// {
// "zone": "masterzone.example",
// "primary": {
// "enabled": true,
// "secondaries": [
// {
// "ip": "1.2.3.4",
// "port": 53,
// "notify": true
// }
// ]
// }
// }
}
func ExampleRecord() {
// Construct the A record
record := dns.NewRecord("test.com", "a", "A")
record.TTL = 300
// Construct primary answer(higher priority)
pAns := dns.NewAv4Answer("1.1.1.1")
pAns.Meta.Priority = 1
pAns.Meta.Up = data.FeedPtr{FeedID: "feed1_id"}
// Construct secondary answer(lower priority)
sAns := dns.NewAv4Answer("2.2.2.2")
sAns.Meta.Priority = 2
sAns.Meta.Up = data.FeedPtr{FeedID: "feed2_id"}
// Add both answers to record
record.AddAnswer(pAns)
record.AddAnswer(sAns)
// Construct and add both filters to the record(ORDER MATTERS)
record.AddFilter(filter.NewUp())
record.AddFilter(filter.NewSelFirstN(1))
// Add region 'test' to record(set as down)
record.Regions["test"] = data.Region{Meta: data.Meta{Up: false}}
fmt.Println(record)
fmt.Println(record.TTL)
fmt.Println("Primary answer:")
fmt.Println(record.Answers[0])
fmt.Println(record.Answers[0].Meta.Priority)
fmt.Println(record.Answers[0].Meta.Up)
fmt.Println("Secondary answer:")
fmt.Println(record.Answers[1])
fmt.Println(record.Answers[1].Meta.Priority)
fmt.Println(record.Answers[1].Meta.Up)
fmt.Println("First Filter in Chain:")
fmt.Println(record.Filters[0].Type)
fmt.Println(record.Filters[0].Config)
fmt.Println("Second Filter in Chain:")
fmt.Println(record.Filters[1].Type)
fmt.Println(record.Filters[1].Config)
fmt.Println("Regions:")
fmt.Println(record.Regions["test"].Meta.Up)
// Output:
// a.test.com A
// 300
// Primary answer:
// 1.1.1.1
// 1
// {feed1_id}
// Secondary answer:
// 2.2.2.2
// 2
// {feed2_id}
// First Filter in Chain:
// up
// map[]
// Second Filter in Chain:
// select_first_n
// map[N:1]
// Regions:
// false
}
func ExampleRecordLink() {
// Construct the src record
srcRecord := dns.NewRecord("test.com", "a", "A")
srcRecord.TTL = 300
srcRecord.Meta.Priority = 2
linkedRecord := dns.NewRecord("test.com", "l", "A")
linkedRecord.LinkTo(srcRecord.Domain)
fmt.Println(linkedRecord)
fmt.Println(linkedRecord.Meta)
fmt.Println(linkedRecord.Answers)
// Output:
// l.test.com A
// <nil>
// []
}

76
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/record.go generated vendored Normal file
View File

@ -0,0 +1,76 @@
package dns
import (
"fmt"
"strings"
"gopkg.in/ns1/ns1-go.v2/rest/model/data"
"gopkg.in/ns1/ns1-go.v2/rest/model/filter"
)
// Record wraps an NS1 /zone/{zone}/{domain}/{type} resource
type Record struct {
Meta *data.Meta `json:"meta,omitempty"`
ID string `json:"id,omitempty"`
Zone string `json:"zone"`
Domain string `json:"domain"`
Type string `json:"type"`
Link string `json:"link,omitempty"`
TTL int `json:"ttl,omitempty"`
UseClientSubnet *bool `json:"use_client_subnet,omitempty"`
// Answers must all be of the same type as the record.
Answers []*Answer `json:"answers"`
// The records' filter chain.
Filters []*filter.Filter `json:"filters,omitempty"`
// The records' regions.
Regions data.Regions `json:"regions,omitempty"`
}
func (r Record) String() string {
return fmt.Sprintf("%s %s", r.Domain, r.Type)
}
// NewRecord takes a zone, domain and record type t and creates a *Record with
// UseClientSubnet: true & empty Answers.
func NewRecord(zone string, domain string, t string) *Record {
if !strings.HasSuffix(domain, zone) {
domain = fmt.Sprintf("%s.%s", domain, zone)
}
return &Record{
Meta: &data.Meta{},
Zone: zone,
Domain: domain,
Type: t,
Answers: []*Answer{},
Regions: data.Regions{},
}
}
// LinkTo sets a Record Link to an FQDN.
// to is the FQDN of the target record whose config should be used. Does
// not have to be in the same zone.
func (r *Record) LinkTo(to string) {
r.Meta = nil
r.Answers = []*Answer{}
r.Link = to
}
// AddAnswer adds an answer to the record.
func (r *Record) AddAnswer(ans *Answer) {
if r.Answers == nil {
r.Answers = []*Answer{}
}
r.Answers = append(r.Answers, ans)
}
// AddFilter adds a filter to the records' filter chain(ordering of filters matters).
func (r *Record) AddFilter(fil *filter.Filter) {
if r.Filters == nil {
r.Filters = []*filter.Filter{}
}
r.Filters = append(r.Filters, fil)
}

157
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/zone.go generated vendored Normal file
View File

@ -0,0 +1,157 @@
package dns
import "gopkg.in/ns1/ns1-go.v2/rest/model/data"
// Zone wraps an NS1 /zone resource
type Zone struct {
// Zones have metadata tables, but no filters act on 'zone-level' meta.
Meta *data.Meta `json:"meta,omitempty"`
// Read-only fields
DNSServers []string `json:"dns_servers,omitempty"`
NetworkPools []string `json:"network_pools,omitempty"`
Pool string `json:"pool,omitempty"` // Deprecated
ID string `json:"id,omitempty"`
Zone string `json:"zone,omitempty"`
TTL int `json:"ttl,omitempty"`
NxTTL int `json:"nx_ttl,omitempty"`
Retry int `json:"retry,omitempty"`
Serial int `json:"serial,omitempty"`
Refresh int `json:"refresh,omitempty"`
Expiry int `json:"expiry,omitempty"`
Hostmaster string `json:"hostmaster,omitempty"`
// If this is a linked zone, Link points to an existing standard zone,
// reusing its configuration and records. Link is a zones' domain name.
Link *string `json:"link,omitempty"`
// Networks contains the network ids the zone is available. Most zones
// will be in the NSONE Global Network(which is id 0).
NetworkIDs []int `json:"networks,omitempty"`
Records []*ZoneRecord `json:"records,omitempty"`
// Primary contains info to enable slaving of the zone by third party dns servers.
Primary *ZonePrimary `json:"primary,omitempty"`
// Secondary contains info for slaving the zone to a primary dns server.
Secondary *ZoneSecondary `json:"secondary,omitempty"`
}
func (z Zone) String() string {
return z.Zone
}
// ZoneRecord wraps Zone's "records" attribute
type ZoneRecord struct {
Domain string `json:"Domain,omitempty"`
ID string `json:"id,omitempty"`
Link string `json:"link,omitempty"`
ShortAns []string `json:"short_answers,omitempty"`
Tier int `json:"tier,omitempty"`
TTL int `json:"ttl,omitempty"`
Type string `json:"type,omitempty"`
}
// ZonePrimary wraps a Zone's "primary" attribute
type ZonePrimary struct {
// Enabled determines whether AXFR queries (and optionally NOTIFY messages)
// will be enabled for the zone.
Enabled bool `json:"enabled"`
Secondaries []ZoneSecondaryServer `json:"secondaries"`
}
// ZoneSecondaryServer wraps elements of a Zone's "primary.secondary" attribute
type ZoneSecondaryServer struct {
// Read-Only
NetworkIDs []int `json:"networks,omitempty"`
IP string `json:"ip"`
Port int `json:"port,omitempty"`
Notify bool `json:"notify"`
}
// ZoneSecondary wraps a Zone's "secondary" attribute
type ZoneSecondary struct {
// Read-Only fields
Expired bool `json:"expired,omitempty"`
LastXfr int `json:"last_xfr,omitempty"`
Status string `json:"status,omitempty"`
Error *string `json:"error"`
PrimaryIP string `json:"primary_ip,omitempty"`
PrimaryPort int `json:"primary_port,omitempty"`
Enabled bool `json:"enabled"`
TSIG *TSIG `json:"tsig"`
}
// TSIG is a zones transaction signature.
type TSIG struct {
// Key is the encrypted TSIG key(read-only)
Key string `json:"key,omitempty"`
// Whether TSIG is enabled for a secondary zone.
Enabled bool `json:"enabled,omitempty"`
// Which hashing algorithm
Hash string `json:"hash,omitempty"`
// Name of the TSIG key
Name string `json:"name,omitempty"`
}
// NewZone takes a zone domain name and creates a new zone.
func NewZone(zone string) *Zone {
z := Zone{
Zone: zone,
}
return &z
}
// MakePrimary enables Primary, disables Secondary, and sets primary's
// Secondaries to all provided ZoneSecondaryServers
func (z *Zone) MakePrimary(secondaries ...ZoneSecondaryServer) {
z.Secondary = nil
z.Primary = &ZonePrimary{
Enabled: true,
Secondaries: secondaries,
}
if z.Primary.Secondaries == nil {
z.Primary.Secondaries = make([]ZoneSecondaryServer, 0)
}
}
// MakeSecondary enables Secondary, disables Primary, and sets secondary's
// Primary_ip to provided ip.
func (z *Zone) MakeSecondary(ip string) {
z.Secondary = &ZoneSecondary{
Enabled: true,
PrimaryIP: ip,
PrimaryPort: 53,
}
z.Primary = &ZonePrimary{
Enabled: false,
Secondaries: make([]ZoneSecondaryServer, 0),
}
}
// LinkTo sets Link to a target zone domain name and unsets all other configuration properties.
// No other zone configuration properties (such as refresh, retry, etc) may be specified,
// since they are all pulled from the target zone. Linked zones, once created, cannot be
// configured at all and cannot have records added to them. They may only be deleted, which
// does not affect the target zone at all.
func (z *Zone) LinkTo(to string) {
z.Meta = nil
z.TTL = 0
z.NxTTL = 0
z.Retry = 0
z.Refresh = 0
z.Expiry = 0
z.Primary = nil
z.DNSServers = nil
z.NetworkIDs = nil
z.NetworkPools = nil
z.Hostmaster = ""
z.Pool = ""
z.Secondary = nil
z.Link = &to
}

2
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/doc.go generated vendored Normal file
View File

@ -0,0 +1,2 @@
// Package model defines structures for interacting with the NS1 API.
package model

View File

@ -0,0 +1,2 @@
// Package filter contains definitions for NS1 filter chains.
package filter

View File

@ -0,0 +1,182 @@
package filter
// Filter wraps the values of a Record's "filters" attribute
type Filter struct {
Type string `json:"filter"`
Disabled bool `json:"disabled,omitempty"`
Config Config `json:"config"`
}
// Enable a filter.
func (f *Filter) Enable() {
f.Disabled = false
}
// Disable a filter.
func (f *Filter) Disable() {
f.Disabled = true
}
// Config is a flat mapping where values are simple (no slices/maps).
type Config map[string]interface{}
// NewSelFirstN returns a filter that eliminates all but the
// first N answers from the list.
func NewSelFirstN(n int) *Filter {
return &Filter{
Type: "select_first_n",
Config: Config{"N": n},
}
}
// NewShuffle returns a filter that randomly sorts the answers.
func NewShuffle() *Filter {
return &Filter{Type: "shuffle", Config: Config{}}
}
// GEOGRAPHICAL FILTERS
// NewSelFirstRegion returns a filter that keeps only the answers
// that are in the same region as the first answer.
func NewSelFirstRegion() *Filter {
return &Filter{Type: "select_first_n", Config: Config{}}
}
// NewStickyRegion first sorts regions uniquely depending on the IP
// address of the requester, and then groups all answers together by
// region. The same requester always gets the same ordering of regions,
// but answers within each region may be in any order. byNetwork indicates
// whether to apply the 'stickyness' by subnet(not individual IP).
func NewStickyRegion(byNetwork bool) *Filter {
return &Filter{
Type: "sticky_region",
Config: Config{"sticky_by_network": byNetwork},
}
}
// NewGeofenceCountry returns a filter that fences using "country",
// "us_state", and "ca_province" metadata fields in answers. Only
// answers in the same country/state/province as the user (or
// answers with no specified location) are returned. rmNoLoc determines
// whether to remove answers without location on any match.
func NewGeofenceCountry(rmNoLoc bool) *Filter {
return &Filter{
Type: "geofence_country",
Config: Config{"remove_no_location": rmNoLoc},
}
}
// NewGeofenceRegional returns a filter that restricts to answers in
// same geographical region as requester. rmNoGeo determines whether
// to remove answers without georegion on any match.
func NewGeofenceRegional(rmNoGeo bool) *Filter {
return &Filter{
Type: "geofence_regional",
Config: Config{"remove_no_georegion": rmNoGeo},
}
}
// NewGeotargetCountry returns a filter that sorts answers by distance
// to requester by country, US state, and/or Canadian province.
func NewGeotargetCountry() *Filter {
return &Filter{Type: "geofence_country", Config: Config{}}
}
// NewGeotargetLatLong returns a filter that sorts answers by distance
// to user using lat/long.
func NewGeotargetLatLong() *Filter {
return &Filter{Type: "geotarget_latlong", Config: Config{}}
}
// NewGeotargetRegional returns a filter that sorts answers by distance
// to user by geographical region.
func NewGeotargetRegional() *Filter {
return &Filter{Type: "geotarget_regional", Config: Config{}}
}
// NETWORK FILTERS
// NewSticky returns a filter that sorts answers uniquely depending
// on the IP address of the requester. The same requester always
// gets the same ordering of answers. byNetwork indicates whether
// to apply the 'stickyness' by subnet(not individual IP).
func NewSticky(byNetwork bool) *Filter {
return &Filter{
Type: "sticky",
Config: Config{"sticky_by_network": byNetwork},
}
}
// NewWeightedSticky returns a filter that shuffles answers randomly
// per-requester based on weight. byNetwork indicates whether to
// apply the 'stickyness' by subnet(not individual IP).
func NewWeightedSticky(byNetwork bool) *Filter {
return &Filter{
Type: "weighted_sticky",
Config: Config{"sticky_by_network": byNetwork},
}
}
// NewIPv4PrefixShuffle returns a filter that randomly selects
// IPv4 addresses from prefix list. This filter can only be used
// A records. n is the number of IPs to randomly select per answer.
func NewIPv4PrefixShuffle(n int) *Filter {
return &Filter{
Type: "ipv4_prefix_shuffle",
Config: Config{"N": n},
}
}
// NewNetfenceASN returns a filter that restricts to answers where
// the ASN of requester IP matches ASN list. rmNoASN determines
// whether to remove answers without asn list on any match.
func NewNetfenceASN(rmNoASN bool) *Filter {
return &Filter{
Type: "netfence_asn",
Config: Config{"remove_no_asn": rmNoASN},
}
}
// NewNetfencePrefix returns a filter that restricts to answers where
// requester IP matches prefix list. rmNoIPPrefix determines
// whether to remove answers without ip prefixes on any match.
func NewNetfencePrefix(rmNoIPPrefix bool) *Filter {
return &Filter{
Type: "netfence_prefix",
Config: Config{"remove_no_ip_prefixes": rmNoIPPrefix},
}
}
// STATUS FILTERS
// NewUp returns a filter that eliminates all answers where
// the 'up' metadata field is not true.
func NewUp() *Filter {
return &Filter{Type: "up", Config: Config{}}
}
// NewPriority returns a filter that fails over according to
// prioritized answer tiers.
func NewPriority() *Filter {
return &Filter{Type: "priority", Config: Config{}}
}
// NewShedLoad returns a filter that "sheds" traffic to answers
// based on load, using one of several load metrics. You must set
// values for low_watermark, high_watermark, and the configured
// load metric, for each answer you intend to subject to load
// shedding.
func NewShedLoad(metric string) *Filter {
return &Filter{
Type: "shed_load",
Config: Config{"metric": metric},
}
}
// TRAFFIC FILTERS
// NewWeightedShuffle returns a filter that shuffles answers
// randomly based on their weight.
func NewWeightedShuffle() *Filter {
return &Filter{Type: "weighted_shuffle", Config: Config{}}
}

View File

@ -0,0 +1,4 @@
package monitor
// Config is a flat mapping where values are simple (no slices/maps).
type Config map[string]interface{}

View File

@ -0,0 +1,2 @@
// Package monitor contains definitions for NS1 monitoring jobs.
package monitor

172
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/job.go generated vendored Normal file
View File

@ -0,0 +1,172 @@
package monitor
// Job wraps an NS1 /monitoring/jobs resource
type Job struct {
ID string `json:"id,omitempty"`
// The id of the notification list to send notifications to.
NotifyListID string `json:"notify_list"`
// Type of monitor to be run.
// Available job types:
// - http: Do an HTTP request against a webserver
// - dns: Do a DNS lookup against a nameserver
// - tcp: Connect to a TCP port on a host
// - ping: Ping a host using ICMP packets
Type string `json:"job_type"`
// Configuration dictionary(key/vals depend on the jobs' type).
Config Config `json:"config"`
// The current status of the monitor.
Status map[string]Status `json:"status,omitempty"`
// Rules for determining failure conditions.
Rules []*Rule `json:"rules"`
// List of regions in which to run the monitor.
// eg, ["dal", "sin", "sjc", "lga", "ams"]
Regions []string `json:"regions"`
// Indicates if the job is active or temporarily disabled.
Active bool `json:"active"`
// Frequency(in seconds), at which to run the monitor.
Frequency int `json:"frequency"`
// The policy for determining the monitor's global status based
// on the status of the job in all regions.
// Available policies:
// - quorum: Status change when majority status
// - all: Status change only when all regions are in agreement
// - one: Status change if any region changes
Policy string `json:"policy"`
// Controls behavior of how the job is assigned to monitoring regions.
// Currently this must be fixed — indicating monitoring regions are explicitly chosen.
RegionScope string `json:"region_scope"`
// Freeform notes to be included in any notifications about this job,
// e.g., instructions for operators who will receive the notifications.
Notes string `json:"notes,omitempty"`
// A free-form display name for the monitoring job.
Name string `json:"name"`
// Time(in seconds) between repeat notifications of a failed job.
// Set to 0 to disable repeating notifications.
NotifyRepeat int `json:"notify_repeat"`
// If true, on any apparent state change, the job is quickly re-run after
// one second to confirm the state change before notification.
RapidRecheck bool `json:"rapid_recheck"`
// Time(in seconds) after a failure to wait before sending a notification.
NotifyDelay int `json:"notify_delay"`
// If true, notifications are sent for any regional failure (and failback if desired),
// in addition to global state notifications.
NotifyRegional bool `json:"notidy_regional"`
// If true, a notification is sent when a job returns to an "up" state.
NotifyFailback bool `json:"notify_failback"`
}
// Activate a monitoring job.
func (j *Job) Activate() {
j.Active = true
}
// Deactivate a monitoring job.
func (j *Job) Deactivate() {
j.Active = false
}
// Result wraps an element of a JobType's "results" attribute
type Result struct {
Comparators []string `json:"comparators"`
Metric bool `json:"metric"`
Validator string `json:"validator"`
ShortDesc string `json:"shortdesc"`
Type string `json:"type"`
Desc string `json:"desc"`
}
// Status wraps an value of a Job's "status" attribute
type Status struct {
Since int `json:"since"`
Status string `json:"status"`
}
// Rule wraps an element of a Job's "rules" attribute
type Rule struct {
Key string `json:"key"`
Value interface{} `json:"value"`
Comparison string `json:"comparison"`
}
// NewHTTPConfig constructs/returns a job configuration for HTTP type jobs.
// url is the URL to query. (Required)
// method is the HTTP method(valid methods are HEAD, GET, and POST).
// ua is the user agent text in the request header.
// auth is the authorization header to use in request.
// connTimeout is the timeout(in sec) to wait for query output.
func NewHTTPConfig(url, method, ua, auth string, connTimeout int) *Config {
return &Config{
"url": url, // Required
"method": method,
"user_agent": ua,
"auth": auth,
"connection_timeout": connTimeout,
}
}
// NewDNSConfig constructs/returns a job configuration for DNS type jobs.
// host is the IP address or hostname of the nameserver to query. (Required)
// domain name to query. (Required)
// port is the dns port to query on host.
// t is the type of the DNS record type to query.
// respTimeout is the timeout(in ms) after sending query to wait for the output.
func NewDNSConfig(host, domain string, port int, t string, respTimeout int) *Config {
return &Config{
"host": host, // Required
"domain": domain, // Required
"port": port,
"type": t,
"response_timeout": respTimeout,
}
}
// NewTCPConfig constructs/returns a job configuration for TCP type jobs.
// host is the IP address or hostname to connect to. (Required)
// port is the tcp port to connect to on host. (Required)
// connTimeout is the timeout(in ms) before giving up on trying to connect.
// respTimeout is the timeout(in sec) after connecting to wait for output.
// send is the string to send to the host upon connecting.
// ssl determines whether to attempt negotiating an SSL connection.
func NewTCPConfig(host string, port, connTimeout, respTimeout int, send string, ssl bool) *Config {
return &Config{
"host": host, // Required
"port": port, // Required
"connection_timeout": connTimeout,
"response_timeout": respTimeout,
"send": send,
"ssl": ssl,
}
}
// NewPINGConfig constructs/returns a job configuration for PING type jobs.
// host is the IP address or hostname to ping. (Required)
// timeout is the timeout(in ms) before marking the host as failed.
// count is the number of packets to send.
// interval is the minimum time(in ms) to wait between sending each packet.
func NewPINGConfig(host string, timeout, count, interval int) *Config {
return &Config{
"host": host, // Required
"timeout": timeout,
"count": count,
"interval": interval,
}
}

View File

@ -0,0 +1,97 @@
package monitor
import (
"encoding/json"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnmarshalJobs(t *testing.T) {
data := []byte(`[
{
"id": "52a27d4397d5f07003fdbe7b",
"config": {
"host": "1.2.3.4"
},
"status": {
"lga": {
"since": 1389407609,
"status": "up"
},
"global": {
"since": 1389407609,
"status": "up"
},
"sjc": {
"since": 1389404014,
"status": "up"
}
},
"rules": [
{
"key": "rtt",
"value": 100,
"comparison": "<"
}
],
"job_type": "ping",
"regions": [
"lga",
"sjc"
],
"active": true,
"frequency": 60,
"policy": "quorum",
"region_scope": "fixed"
}
]`)
mjl := []*Job{}
if err := json.Unmarshal(data, &mjl); err != nil {
t.Error(err)
}
if len(mjl) != 1 {
fmt.Println(mjl)
t.Error("Do not have any jobs")
}
j := mjl[0]
if j.ID != "52a27d4397d5f07003fdbe7b" {
t.Error("Wrong ID")
}
conf := j.Config
if conf["host"] != "1.2.3.4" {
t.Error("Wrong host")
}
status := j.Status["global"]
if status.Since != 1389407609 {
t.Error("since has unexpected value")
}
if status.Status != "up" {
t.Error("Status is not up")
}
r := j.Rules[0]
assert.Equal(t, r.Key, "rtt", "RTT rule key is wrong")
assert.Equal(t, r.Value.(float64), float64(100), "RTT rule value is wrong")
if r.Comparison != "<" {
t.Error("RTT rule comparison is wrong")
}
if j.Type != "ping" {
t.Error("Jobtype is wrong")
}
if j.Regions[0] != "lga" {
t.Error("First region is not lga")
}
if !j.Active {
t.Error("Job is not active")
}
if j.Frequency != 60 {
t.Error("Job frequency != 60")
}
if j.Policy != "quorum" {
t.Error("Job policy is not quorum")
}
if j.RegionScope != "fixed" {
t.Error("Job region scope is not fixed")
}
}

View File

@ -0,0 +1,72 @@
package monitor
// NotifyList wraps notifications.
type NotifyList struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Notifications []*Notification `json:"notify_list,omitempty"`
}
// Notification represents endpoint to alert to.
type Notification struct {
Type string `json:"type,omitempty"`
Config Config `json:"config,omitempty"`
}
// NewNotifyList returns a notify list that alerts via the given notifications.
func NewNotifyList(name string, nl ...*Notification) *NotifyList {
if nl == nil {
nl = []*Notification{}
}
return &NotifyList{Name: name, Notifications: nl}
}
// NewUserNotification returns a notification that alerts via user.
func NewUserNotification(username string) *Notification {
return &Notification{
Type: "user",
Config: Config{"user": username}}
}
// NewEmailNotification returns a notification that alerts via email.
func NewEmailNotification(email string) *Notification {
return &Notification{
Type: "email",
Config: Config{"email": email}}
}
// NewFeedNotification returns a notification that alerts via datafeed.
func NewFeedNotification(sourceID string) *Notification {
return &Notification{
Type: "datafeed",
Config: Config{"sourceid": sourceID}}
}
// NewWebNotification returns a notification that alerts via webhook.
func NewWebNotification(url string) *Notification {
return &Notification{
Type: "webhook",
Config: Config{"url": url}}
}
// NewPagerDutyNotification returns a notification that alerts via pagerduty.
func NewPagerDutyNotification(key string) *Notification {
return &Notification{
Type: "pagerduty",
Config: Config{"service_key": key}}
}
// NewHipChatNotification returns a notification that alerts via hipchat.
func NewHipChatNotification(token, room string) *Notification {
return &Notification{
Type: "hipchat",
Config: Config{"token": token, "room": room}}
}
// NewSlackNotification returns a notification that alerts via slack.
func NewSlackNotification(url, username, channel string) *Notification {
return &Notification{
Type: "slack",
Config: Config{"url": url, "username": username, "channel": channel}}
}

15
vendor/gopkg.in/ns1/ns1-go.v2/rest/model/stat.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
package model
// // GetQPSStats returns current queries per second (QPS) for the account
// func (c APIClient) GetQPSStats() (v float64, err error) {
// var s map[string]float64
// _, err = c.doHTTPUnmarshal("GET", "https://api.nsone.net/v1/stats/qps", nil, &s)
// if err != nil {
// return v, err
// }
// v, found := s["qps"]
// if !found {
// return v, errors.New("Could not find 'qps' key in returned data")
// }
// return v, nil
// }

108
vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_job.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
package rest
import (
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/monitor"
)
// JobsService handles 'monitoring/jobs' endpoint.
type JobsService service
// List returns all monitoring jobs for the account.
//
// NS1 API docs: https://ns1.com/api/#jobs-get
func (s *JobsService) List() ([]*monitor.Job, *http.Response, error) {
req, err := s.client.NewRequest("GET", "monitoring/jobs", nil)
if err != nil {
return nil, nil, err
}
mjl := []*monitor.Job{}
resp, err := s.client.Do(req, &mjl)
if err != nil {
return nil, resp, err
}
return mjl, resp, nil
}
// Get takes an ID and returns details for a specific monitoring job.
//
// NS1 API docs: https://ns1.com/api/#jobs-jobid-get
func (s *JobsService) Get(id string) (*monitor.Job, *http.Response, error) {
path := fmt.Sprintf("%s/%s", "monitoring/jobs", id)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var mj monitor.Job
resp, err := s.client.Do(req, &mj)
if err != nil {
return nil, resp, err
}
return &mj, resp, nil
}
// Create takes a *MonitoringJob and creates a new monitoring job.
//
// NS1 API docs: https://ns1.com/api/#jobs-put
func (s *JobsService) Create(mj *monitor.Job) (*http.Response, error) {
path := fmt.Sprintf("%s/%s", "monitoring/jobs", mj.ID)
req, err := s.client.NewRequest("PUT", path, &mj)
if err != nil {
return nil, err
}
// Update mon jobs' fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &mj)
if err != nil {
return resp, err
}
return resp, nil
}
// Update takes a *MonitoringJob and change the configuration details of an existing monitoring job.
//
// NS1 API docs: https://ns1.com/api/#jobs-jobid-post
func (s *JobsService) Update(mj *monitor.Job) (*http.Response, error) {
path := fmt.Sprintf("%s/%s", "monitoring/jobs", mj.ID)
req, err := s.client.NewRequest("POST", path, &mj)
if err != nil {
return nil, err
}
// Update mon jobs' fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &mj)
if err != nil {
return resp, err
}
return resp, nil
}
// Delete takes an ID and immediately terminates and deletes and existing monitoring job.
//
// NS1 API docs: https://ns1.com/api/#jobs-jobid-delete
func (s *JobsService) Delete(id string) (*http.Response, error) {
path := fmt.Sprintf("%s/%s", "monitoring/jobs", id)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, err
}
return resp, nil
}

128
vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_notify.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/monitor"
)
// NotificationsService handles 'monitoring/lists' endpoint.
type NotificationsService service
// List returns all configured notification lists.
//
// NS1 API docs: https://ns1.com/api/#lists-get
func (s *NotificationsService) List() ([]*monitor.NotifyList, *http.Response, error) {
req, err := s.client.NewRequest("GET", "lists", nil)
if err != nil {
return nil, nil, err
}
nl := []*monitor.NotifyList{}
resp, err := s.client.Do(req, &nl)
if err != nil {
return nil, resp, err
}
return nl, resp, nil
}
// Get returns the details and notifiers associated with a specific notification list.
//
// NS1 API docs: https://ns1.com/api/#lists-listid-get
func (s *NotificationsService) Get(listID string) (*monitor.NotifyList, *http.Response, error) {
path := fmt.Sprintf("%s/%s", "lists", listID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var nl monitor.NotifyList
resp, err := s.client.Do(req, &nl)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "unknown notification list" {
return nil, resp, ErrListMissing
}
default:
return nil, resp, err
}
}
return &nl, resp, nil
}
// Create takes a *NotifyList and creates a new notify list.
//
// NS1 API docs: https://ns1.com/api/#lists-put
func (s *NotificationsService) Create(nl *monitor.NotifyList) (*http.Response, error) {
req, err := s.client.NewRequest("PUT", "lists", &nl)
if err != nil {
return nil, err
}
// Update notify list fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &nl)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == fmt.Sprintf("notification list with name \"%s\" exists", nl.Name) {
return resp, ErrListExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update adds or removes entries or otherwise update a notification list.
//
// NS1 API docs: https://ns1.com/api/#list-listid-post
func (s *NotificationsService) Update(nl *monitor.NotifyList) (*http.Response, error) {
path := fmt.Sprintf("%s/%s", "lists", nl.ID)
req, err := s.client.NewRequest("POST", path, &nl)
if err != nil {
return nil, err
}
// Update mon lists' fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &nl)
if err != nil {
return resp, err
}
return resp, nil
}
// Delete immediately deletes an existing notification list.
//
// NS1 API docs: https://ns1.com/api/#lists-listid-delete
func (s *NotificationsService) Delete(listID string) (*http.Response, error) {
path := fmt.Sprintf("%s/%s", "lists", listID)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, err
}
return resp, nil
}
var (
// ErrListExists bundles PUT create error.
ErrListExists = errors.New("Notify List already exists.")
// ErrListMissing bundles GET/POST/DELETE error.
ErrListMissing = errors.New("Notify List does not exist.")
)

134
vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go generated vendored Normal file
View File

@ -0,0 +1,134 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
// RecordsService handles 'zones/ZONE/DOMAIN/TYPE' endpoint.
type RecordsService service
// Get takes a zone, domain and record type t and returns full configuration for a DNS record.
//
// NS1 API docs: https://ns1.com/api/#record-get
func (s *RecordsService) Get(zone, domain, t string) (*dns.Record, *http.Response, error) {
path := fmt.Sprintf("zones/%s/%s/%s", zone, domain, t)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var r dns.Record
resp, err := s.client.Do(req, &r)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "record not found" {
return nil, resp, ErrRecordMissing
}
default:
return nil, resp, err
}
}
return &r, resp, nil
}
// Create takes a *Record and creates a new DNS record in the specified zone, for the specified domain, of the given record type.
//
// The given record must have at least one answer.
// NS1 API docs: https://ns1.com/api/#record-put
func (s *RecordsService) Create(r *dns.Record) (*http.Response, error) {
path := fmt.Sprintf("zones/%s/%s/%s", r.Zone, r.Domain, r.Type)
req, err := s.client.NewRequest("PUT", path, &r)
if err != nil {
return nil, err
}
// Update record fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &r)
if err != nil {
switch err.(type) {
case *Error:
switch err.(*Error).Message {
case "zone not found":
return resp, ErrZoneMissing
case "record already exists":
return resp, ErrRecordExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update takes a *Record and modifies configuration details for an existing DNS record.
//
// Only the fields to be updated are required in the given record.
// NS1 API docs: https://ns1.com/api/#record-post
func (s *RecordsService) Update(r *dns.Record) (*http.Response, error) {
path := fmt.Sprintf("zones/%s/%s/%s", r.Zone, r.Domain, r.Type)
req, err := s.client.NewRequest("POST", path, &r)
if err != nil {
return nil, err
}
// Update records fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &r)
if err != nil {
switch err.(type) {
case *Error:
switch err.(*Error).Message {
case "zone not found":
return resp, ErrZoneMissing
case "record already exists":
return resp, ErrRecordExists
}
default:
return resp, err
}
}
return resp, nil
}
// Delete takes a zone, domain and record type t and removes an existing record and all associated answers and configuration details.
//
// NS1 API docs: https://ns1.com/api/#record-delete
func (s *RecordsService) Delete(zone string, domain string, t string) (*http.Response, error) {
path := fmt.Sprintf("zones/%s/%s/%s", zone, domain, t)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "record not found" {
return resp, ErrRecordMissing
}
default:
return resp, err
}
}
return resp, nil
}
var (
// ErrRecordExists bundles PUT create error.
ErrRecordExists = errors.New("Record already exists.")
// ErrRecordMissing bundles GET/POST/DELETE error.
ErrRecordMissing = errors.New("Record does not exist.")
)

15
vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
package rest
// // GetQPSStats returns current queries per second (QPS) for the account
// func (c APIClient) GetQPSStats() (v float64, err error) {
// var s map[string]float64
// _, err = c.doHTTPUnmarshal("GET", "https://api.nsone.net/v1/stats/qps", nil, &s)
// if err != nil {
// return v, err
// }
// v, found := s["qps"]
// if !found {
// return v, errors.New("Could not find 'qps' key in returned data")
// }
// return v, nil
// }

42
vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package rest
import (
"log"
"net/http"
)
// DoerFunc satisfies Interface. DoerFuncs are useful for adding
// logging/instrumentation to the http.Client that is used
// within the rest.APIClient.
type DoerFunc func(*http.Request) (*http.Response, error)
// Do is implementation of rest.Doer interface. Calls itself on the
// given http.Request.
func (f DoerFunc) Do(r *http.Request) (*http.Response, error) {
return f(r)
}
// A Decorator wraps a Doer with extra behavior, and doesnt
// affect the behavior of other instances of the same type.
type Decorator func(Doer) Doer
// Decorate decorates a Doer c with all the given Decorators, in order.
// Core object(Doer instance) that we want to apply layers(Decorator slice) to.
func Decorate(d Doer, ds ...Decorator) Doer {
decorated := d
for _, decorate := range ds {
decorated = decorate(decorated)
}
return decorated
}
// Logging returns a Decorator that logs a Doer's requests.
// Dependency injection for the logger instance(inside the closures environment).
func Logging(l *log.Logger) Decorator {
return func(d Doer) Doer {
return DoerFunc(func(r *http.Request) (*http.Response, error) {
l.Printf("%s: %s %s", r.UserAgent(), r.Method, r.URL)
return d.Do(r)
})
}
}

144
vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go generated vendored Normal file
View File

@ -0,0 +1,144 @@
package rest
import (
"errors"
"fmt"
"net/http"
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
)
// ZonesService handles 'zones' endpoint.
type ZonesService service
// List returns all active zones and basic zone configuration details for each.
//
// NS1 API docs: https://ns1.com/api/#zones-get
func (s *ZonesService) List() ([]*dns.Zone, *http.Response, error) {
req, err := s.client.NewRequest("GET", "zones", nil)
if err != nil {
return nil, nil, err
}
zl := []*dns.Zone{}
resp, err := s.client.Do(req, &zl)
if err != nil {
return nil, resp, err
}
return zl, resp, nil
}
// Get takes a zone name and returns a single active zone and its basic configuration details.
//
// NS1 API docs: https://ns1.com/api/#zones-zone-get
func (s *ZonesService) Get(zone string) (*dns.Zone, *http.Response, error) {
path := fmt.Sprintf("zones/%s", zone)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
var z dns.Zone
resp, err := s.client.Do(req, &z)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "zone not found" {
return nil, resp, ErrZoneMissing
}
default:
return nil, resp, err
}
}
return &z, resp, nil
}
// Create takes a *Zone and creates a new DNS zone.
//
// NS1 API docs: https://ns1.com/api/#zones-put
func (s *ZonesService) Create(z *dns.Zone) (*http.Response, error) {
path := fmt.Sprintf("zones/%s", z.Zone)
req, err := s.client.NewRequest("PUT", path, &z)
if err != nil {
return nil, err
}
// Update zones fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &z)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "zone already exists" {
return resp, ErrZoneExists
}
default:
return resp, err
}
}
return resp, nil
}
// Update takes a *Zone and modifies basic details of a DNS zone.
//
// NS1 API docs: https://ns1.com/api/#zones-post
func (s *ZonesService) Update(z *dns.Zone) (*http.Response, error) {
path := fmt.Sprintf("zones/%s", z.Zone)
req, err := s.client.NewRequest("POST", path, &z)
if err != nil {
return nil, err
}
// Update zones fields with data from api(ensure consistent)
resp, err := s.client.Do(req, &z)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "zone not found" {
return resp, ErrZoneMissing
}
default:
return resp, err
}
}
return resp, nil
}
// Delete takes a zone and destroys an existing DNS zone and all records in the zone.
//
// NS1 API docs: https://ns1.com/api/#zones-delete
func (s *ZonesService) Delete(zone string) (*http.Response, error) {
path := fmt.Sprintf("zones/%s", zone)
req, err := s.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
switch err.(type) {
case *Error:
if err.(*Error).Message == "zone not found" {
return resp, ErrZoneMissing
}
default:
return resp, err
}
}
return resp, nil
}
var (
// ErrZoneExists bundles PUT create error.
ErrZoneExists = errors.New("Zone already exists.")
// ErrZoneMissing bundles GET/POST/DELETE error.
ErrZoneMissing = errors.New("Zone does not exist.")
)

6
vendor/vendor.json vendored
View File

@ -2959,6 +2959,12 @@
"revision": "766e555c68dc8bda90d197ee8946c37519c19409",
"revisionTime": "2017-01-17T13:00:17Z"
},
{
"checksumSHA1": "U0s6Vq/AxUhEEsnORw2DnZ4cw1c=",
"path": "gopkg.in/ns1/ns1-go.v2",
"revision": "d8d10b7f448291ddbdce48d4594fb1b667014c8b",
"revisionTime": "2016-11-05T01:14:08Z"
},
{
"checksumSHA1": "mkLQOQwQwoUc9Kr9+PaVGrKUzI4=",
"path": "gopkg.in/resty.v0",

View File

@ -41,6 +41,7 @@ body.layout-mailgun,
body.layout-mysql,
body.layout-newrelic,
body.layout-nomad,
body.layout-ns1,
body.layout-openstack,
body.layout-opsgenie,
body.layout-packet,

View File

@ -0,0 +1,36 @@
---
layout: "ns1"
page_title: "Provider: NS1"
sidebar_current: "docs-ns1-index"
description: |-
The [NS1](https://ns1.com/) provider is used to interact with the resources supported by NS1.
---
# NS1 Provider
The NS1 provider exposes resources to interact with the NS1 REST API. 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 NS1 provider
provider "ns1" {
apikey = "${var.ns1_apikey}"
}
# Create a new zone
resource "ns1_zone" "foobar" {
...
}
```
## Argument Reference
The following arguments are supported:
* `apikey` - (Required) NS1 API token. It must be provided, but it can also
be sourced from the `NS1_API_KEY` environment variable.

View File

@ -0,0 +1,60 @@
---
layout: "ns1"
page_title: "NS1: ns1_apikey"
sidebar_current: "docs-ns1-resource-apikey"
description: |-
Provides a NS1 Api Key resource.
---
# ns1\_apikey
Provides a NS1 Api Key resource. This can be used to create, modify, and delete api keys.
## Example Usage
```
resource "ns1_team" "example" {
name = "Example team"
}
resource "ns1_apikey" "example" {
name = "Example key"
teams = ["${ns1_team.example.id}"]
permissions = {
dns_view_zones = false
account_manage_users = false
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The free form name of the apikey.
* `key` - (Required) The apikeys authentication token.
* `teams` - (Required) The teams that the apikey belongs to.
* `permissions` - (Optional) The allowed permissions of the apikey. Permissions documented below.
Permissions (`permissions`) support the following:
* `dns_view_zones` - (Optional) Whether the apikey can view the accounts zones.
* `dns_manage_zones` - (Optional) Whether the apikey can modify the accounts zones.
* `dns_zones_allow_by_default` - (Optional) If true, enable the `dns_zones_allow` list, otherwise enable the `dns_zones_deny` list.
* `dns_zones_allow` - (Optional) List of zones that the apikey may access.
* `dns_zones_deny` - (Optional) List of zones that the apikey may not access.
* `data_push_to_datafeeds` - (Optional) Whether the apikey can publish to data feeds.
* `data_manage_datasources` - (Optional) Whether the apikey can modify data sources.
* `data_manage_datafeeds` - (Optional) Whether the apikey can modify data feeds.
* `account_manage_users` - (Optional) Whether the apikey can modify account users.
* `account_manage_payment_methods` - (Optional) Whether the apikey can modify account payment methods.
* `account_manage_plan` - (Optional) Whether the apikey can modify the account plan.
* `account_manage_teams` - (Optional) Whether the apikey can modify other teams in the account.
* `account_manage_apikeys` - (Optional) Whether the apikey can modify account apikeys.
* `account_manage_account_settings` - (Optional) Whether the apikey can modify account settings.
* `account_view_activity_log` - (Optional) Whether the apikey can view activity logs.
* `account_view_invoices` - (Optional) Whether the apikey can view invoices.
* `monitoring_manage_lists` - (Optional) Whether the apikey can modify notification lists.
* `monitoring_manage_jobs` - (Optional) Whether the apikey can modify monitoring jobs.
* `monitoring_view_jobs` - (Optional) Whether the apikey can view monitoring jobs.

View File

@ -0,0 +1,45 @@
---
layout: "ns1"
page_title: "NS1: ns1_datafeed"
sidebar_current: "docs-ns1-resource-datafeed"
description: |-
Provides a NS1 Data Feed resource.
---
# ns1\_datafeed
Provides a NS1 Data Feed resource. This can be used to create, modify, and delete data feeds.
## Example Usage
```
resource "ns1_datasource" "example" {
name = "example"
sourcetype = "nsone_v1"
}
resource "ns1_datafeed" "uswest_feed" {
name = "uswest_feed"
source_id = "${ns1_datasource.example.id}"
config = {
label = "uswest"
}
}
resource "ns1_datafeed" "useast_feed" {
name = "useast_feed"
source_id = "${ns1_datasource.example.id}"
config = {
label = "useast"
}
}
```
## Argument Reference
The following arguments are supported:
* `source_id` - (Required) The data source id that this feed is connected to.
* `name` - (Required) The free form name of the data feed.
* `config` - (Optional) The feeds configuration matching the specification in 'feed\_config' from /data/sourcetypes.

View File

@ -0,0 +1,29 @@
---
layout: "ns1"
page_title: "NS1: ns1_datasource"
sidebar_current: "docs-ns1-resource-datasource"
description: |-
Provides a NS1 Data Source resource.
---
# ns1\_datasource
Provides a NS1 Data Source resource. This can be used to create, modify, and delete data sources.
## Example Usage
```
resource "ns1_datasource" "example" {
name = "example"
sourcetype = "nsone_v1"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The free form name of the data source.
* `sourcetype` - (Required) The data sources type, listed in API endpoint https://api.nsone.net/v1/data/sourcetypes.
* `config` - (Optional) The data source configuration, determined by its type.

View File

@ -0,0 +1,62 @@
---
layout: "ns1"
page_title: "NS1: ns1_monitoringjob"
sidebar_current: "docs-ns1-resource-monitoringjob"
description: |-
Provides a NS1 Monitoring Job resource.
---
# ns1\_monitoringjob
Provides a NS1 Monitoring Job resource. This can be used to create, modify, and delete monitoring jobs.
## Example Usage
```
resource "ns1_monitoringjob" "uswest_monitor" {
name = "uswest"
active = true
regions = ["sjc", "sin", "lga"]
job_type = "tcp"
frequency = 60
rapid_recheck = true
policy = "quorum"
config = {
send = "HEAD / HTTP/1.0\r\n\r\n"
port = 80
host = "example-elb-uswest.aws.amazon.com"
}
rules = {
value = "200 OK"
comparison = "contains"
key = "output"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The free-form display name for the monitoring job.
* `job_type` - (Required) The type of monitoring job to be run.
* `active` - (Required) Indicates if the job is active or temporaril.y disabled.
* `regions` - (Required) The list of region codes in which to run the monitoring job.
* `frequency` - (Required) The frequency, in seconds, at which to run the monitoring job in each region.
* `rapid_recheck` - (Required) If true, on any apparent state change, the job is quickly re-run after one second to confirm the state change before notification.
* `policy` - (Required) The policy for determining the monitor's global status based on the status of the job in all regions.
* `config` - (Required) A configuration dictionary with keys and values depending on the jobs' type.
* `notify_delay` - (Optional) The time in seconds after a failure to wait before sending a notification.
* `notify_repeat` - (Optional) The time in seconds between repeat notifications of a failed job.
* `notify_failback` - (Optional) If true, a notification is sent when a job returns to an "up" state.
* `notify_regional` - (Optional) If true, notifications are sent for any regional failure (and failback if desired), in addition to global state notifications.
* `notify_list` - (Optional) The id of the notification list to send notifications to.
* `notes` - (Optional) Freeform notes to be included in any notifications about this job.
* `rules` - (Optional) A list of rules for determining failure conditions. Job Rules are documented below.
Monitoring Job Rules (`rules`) support the following:
* `key` - (Required) The output key.
* `comparison` - (Required) The comparison to perform on the the output.
* `value` - (Required) The value to compare to.

View File

@ -0,0 +1,62 @@
---
layout: "ns1"
page_title: "NS1: ns1_record"
sidebar_current: "docs-ns1-resource-record"
description: |-
Provides a NS1 Record resource.
---
# ns1\_record
Provides a NS1 Record resource. This can be used to create, modify, and delete records.
## Example Usage
```
resource "ns1_zone" "tld" {
zone = "terraform.example"
}
resource "ns1_record" "www" {
zone = "${ns1_zone.tld.id}"
domain = "www.${ns1_zone.tld.zone}"
type = "CNAME"
ttl = 60
answers = {
answer = ["sub1.${ns1_zone.tld.zone}"]
}
answer = {
answer = ["sub2.${ns1_zone.tld.zone}"]
}
filters = {
filter = "select_first_n"
config = {N=1}
}
}
```
## Argument Reference
The following arguments are supported:
* `zone` - (Required) The zone the record belongs to.
* `domain` - (Required) The records' domain.
* `type` - (Required) The records' RR type.
* `ttl` - (Optional) The records' time to live.
* `link` - (Optional) The target record to link to. This means this record is a 'linked' record, and it inherits all properties from its target.
* `use_client_subnet` - (Optional) Whether to use EDNS client subnet data when available(in filter chain).
* `answers` - (Optional) The list of the RDATA fields for the records' specified type. Answers are documented below.
* `filters` - (Optional) The list of NS1 filters for the record(order matters). Filters are documented below.
Answers (`answers`) support the following:
* `answer` - (Required) List of RDATA fields.
* `region` - (Required) The region this answer belongs to.
Filters (`filters`) support the following:
* `filter` - (Required) The type of filter.
* `disabled` - (Required) Determines whether the filter is applied in the filter chain.
* `config` - (Required) The filters' configuration. Simple key/value pairs determined by the filter type.

View File

@ -0,0 +1,54 @@
---
layout: "ns1"
page_title: "NS1: ns1_team"
sidebar_current: "docs-ns1-resource-team"
description: |-
Provides a NS1 Team resource.
---
# ns1\_team
Provides a NS1 Team resource. This can be used to create, modify, and delete teams.
## Example Usage
```
# Create a new NS1 Team
resource "ns1_team" "example" {
name = "Example team"
permissions = {
dns_view_zones = false
account_manage_users = false
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The free form name of the team.
* `permissions` - (Optional) The allowed permissions of the team. Permissions documented below.
Permissions (`permissions`) support the following:
* `dns_view_zones` - (Optional) Whether the team can view the accounts zones.
* `dns_manage_zones` - (Optional) Whether the team can modify the accounts zones.
* `dns_zones_allow_by_default` - (Optional) If true, enable the `dns_zones_allow` list, otherwise enable the `dns_zones_deny` list.
* `dns_zones_allow` - (Optional) List of zones that the team may access.
* `dns_zones_deny` - (Optional) List of zones that the team may not access.
* `data_push_to_datafeeds` - (Optional) Whether the team can publish to data feeds.
* `data_manage_datasources` - (Optional) Whether the team can modify data sources.
* `data_manage_datafeeds` - (Optional) Whether the team can modify data feeds.
* `account_manage_users` - (Optional) Whether the team can modify account users.
* `account_manage_payment_methods` - (Optional) Whether the team can modify account payment methods.
* `account_manage_plan` - (Optional) Whether the team can modify the account plan.
* `account_manage_teams` - (Optional) Whether the team can modify other teams in the account.
* `account_manage_apikeys` - (Optional) Whether the team can modify account apikeys.
* `account_manage_account_settings` - (Optional) Whether the team can modify account settings.
* `account_view_activity_log` - (Optional) Whether the team can view activity logs.
* `account_view_invoices` - (Optional) Whether the team can view invoices.
* `monitoring_manage_lists` - (Optional) Whether the team can modify notification lists.
* `monitoring_manage_jobs` - (Optional) Whether the team can modify monitoring jobs.
* `monitoring_view_jobs` - (Optional) Whether the team can view monitoring jobs.

View File

@ -0,0 +1,64 @@
---
layout: "ns1"
page_title: "NS1: ns1_user"
sidebar_current: "docs-ns1-resource-user"
description: |-
Provides a NS1 User resource.
---
# ns1\_user
Provides a NS1 User resource. Creating a user sends an invitation email to the user's email address. This can be used to create, modify, and delete users.
## Example Usage
```
resource "ns1_team" "example" {
name = "Example team"
permissions = {
dns_view_zones = false
account_manage_users = false
}
}
resource "ns1_user" "example" {
name = "Example User"
username = "example_user"
email = "user@example.com"
teams = ["${ns1_team.example.id}"]
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The free form name of the user.
* `username` - (Required) The users login name.
* `email` - (Required) The email address of the user.
* `notify` - (Required) The Whether or not to notify the user of specified events. Only `billing` is available currently.
* `teams` - (Required) The teams that the user belongs to.
* `permissions` - (Optional) The allowed permissions of the user. Permissions documented below.
Permissions (`permissions`) support the following:
* `dns_view_zones` - (Optional) Whether the user can view the accounts zones.
* `dns_manage_zones` - (Optional) Whether the user can modify the accounts zones.
* `dns_zones_allow_by_default` - (Optional) If true, enable the `dns_zones_allow` list, otherwise enable the `dns_zones_deny` list.
* `dns_zones_allow` - (Optional) List of zones that the user may access.
* `dns_zones_deny` - (Optional) List of zones that the user may not access.
* `data_push_to_datafeeds` - (Optional) Whether the user can publish to data feeds.
* `data_manage_datasources` - (Optional) Whether the user can modify data sources.
* `data_manage_datafeeds` - (Optional) Whether the user can modify data feeds.
* `account_manage_users` - (Optional) Whether the user can modify account users.
* `account_manage_payment_methods` - (Optional) Whether the user can modify account payment methods.
* `account_manage_plan` - (Optional) Whether the user can modify the account plan.
* `account_manage_teams` - (Optional) Whether the user can modify other teams in the account.
* `account_manage_apikeys` - (Optional) Whether the user can modify account apikeys.
* `account_manage_account_settings` - (Optional) Whether the user can modify account settings.
* `account_view_activity_log` - (Optional) Whether the user can view activity logs.
* `account_view_invoices` - (Optional) Whether the user can view invoices.
* `monitoring_manage_lists` - (Optional) Whether the user can modify notification lists.
* `monitoring_manage_jobs` - (Optional) Whether the user can modify monitoring jobs.
* `monitoring_view_jobs` - (Optional) Whether the user can view monitoring jobs.

View File

@ -0,0 +1,34 @@
---
layout: "ns1"
page_title: "NS1: ns1_zone"
sidebar_current: "docs-ns1-resource-zone"
description: |-
Provides a NS1 Zone resource.
---
# ns1\_zone
Provides a NS1 DNS Zone resource. This can be used to create, modify, and delete zones.
## Example Usage
```
# Create a new DNS zone
resource "ns1_zone" "example" {
zone = "terraform.example.io"
ttl = 600
}
```
## Argument Reference
The following arguments are supported:
* `zone` - (Required) The domain name of the zone.
* `link` - (Optional) The target zone(domain name) to link to.
* `ttl` - (Optional) The SOA TTL.
* `refresh` - (Optional) The SOA Refresh.
* `retry` - (Optional) The SOA Retry.
* `expiry` - (Optional) The SOA Expiry.
* `nx_ttl` - (Optional) The SOA NX TTL.
* `primary` - (Optional) The primary zones' ip. This makes the zone a secondary.

View File

@ -295,6 +295,14 @@
<a href="/docs/providers/newrelic/index.html">New Relic</a>
</li>
<li<%= sidebar_current("docs-providers-nomad") %>>
<a href="/docs/providers/nomad/index.html">Nomad</a>
</li>
<li<%= sidebar_current("docs-providers-ns1") %>>
<a href="/docs/providers/ns1/index.html">NS1</a>
</li>
<li<%= sidebar_current("docs-providers-azurerm") %>>
<a href="/docs/providers/azurerm/index.html">Microsoft Azure</a>
</li>

View File

@ -0,0 +1,47 @@
<% 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">&laquo; Documentation Home</a>
</li>
<li<%= sidebar_current("docs-ns1-index") %>>
<a href="/docs/providers/ns1/index.html">NS1 Provider</a>
</li>
<li<%= sidebar_current(/^docs-ns1-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-ns1-resource-zone") %>>
<a href="/docs/providers/ns1/r/zone.html">ns1_zone</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-record") %>>
<a href="/docs/providers/ns1/r/record.html">ns1_record</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-monitoringjob") %>>
<a href="/docs/providers/ns1/r/monitoringjob.html">ns1_monitoringjob</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-datasource") %>>
<a href="/docs/providers/ns1/r/datasource.html">ns1_datasource</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-datafeed") %>>
<a href="/docs/providers/ns1/r/datafeed.html">ns1_datafeed</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-apikey") %>>
<a href="/docs/providers/ns1/r/apikey.html">ns1_apikey</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-team") %>>
<a href="/docs/providers/ns1/r/team.html">ns1_team</a>
</li>
<li<%= sidebar_current("docs-ns1-resource-user") %>>
<a href="/docs/providers/ns1/r/user.html">ns1_user</a>
</li>
</ul>
</li>
</ul>
</div>
<% end %>
<%= yield %>
<% end %>