From dab0b0bd07e79de744a862f4d52a0d6079111afc Mon Sep 17 00:00:00 2001 From: janeczku Date: Sun, 5 Feb 2017 16:15:33 +0100 Subject: [PATCH] Add support for NS1 DNS provider Closes #48 --- Godeps/Godeps.json | 35 + context.go | 1 + letsencrypt/providers.go | 47 +- .../xenolf/lego/providers/dns/ns1/ns1.go | 97 +++ vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt | 678 ++++++++++++++++++ .../ns1/ns1-go.v2/rest/account_apikey.go | 143 ++++ .../ns1/ns1-go.v2/rest/account_setting.go | 46 ++ .../ns1/ns1-go.v2/rest/account_team.go | 142 ++++ .../ns1/ns1-go.v2/rest/account_user.go | 142 ++++ .../ns1/ns1-go.v2/rest/account_warning.go | 47 ++ vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go | 273 +++++++ .../gopkg.in/ns1/ns1-go.v2/rest/data_feed.go | 116 +++ .../ns1/ns1-go.v2/rest/data_source.go | 126 ++++ vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go | 2 + .../ns1-go.v2/rest/model/account/apikey.go | 13 + .../ns1/ns1-go.v2/rest/model/account/doc.go | 2 + .../rest/model/account/permissions.go | 44 ++ .../ns1-go.v2/rest/model/account/settings.go | 21 + .../ns1/ns1-go.v2/rest/model/account/team.go | 8 + .../ns1/ns1-go.v2/rest/model/account/user.go | 19 + .../ns1-go.v2/rest/model/account/warning.go | 17 + .../ns1/ns1-go.v2/rest/model/data/doc.go | 2 + .../ns1/ns1-go.v2/rest/model/data/feed.go | 38 + .../ns1/ns1-go.v2/rest/model/data/meta.go | 127 ++++ .../ns1/ns1-go.v2/rest/model/data/region.go | 10 + .../ns1/ns1-go.v2/rest/model/data/source.go | 28 + .../ns1/ns1-go.v2/rest/model/dns/answer.go | 101 +++ .../ns1/ns1-go.v2/rest/model/dns/doc.go | 2 + .../ns1/ns1-go.v2/rest/model/dns/record.go | 76 ++ .../ns1/ns1-go.v2/rest/model/dns/zone.go | 157 ++++ .../ns1/ns1-go.v2/rest/model/filter/doc.go | 2 + .../ns1/ns1-go.v2/rest/model/filter/filter.go | 182 +++++ .../ns1-go.v2/rest/model/monitor/config.go | 4 + .../ns1/ns1-go.v2/rest/model/monitor/doc.go | 2 + .../ns1/ns1-go.v2/rest/model/monitor/job.go | 172 +++++ .../ns1-go.v2/rest/model/monitor/notify.go | 72 ++ .../ns1/ns1-go.v2/rest/monitor_job.go | 108 +++ .../ns1/ns1-go.v2/rest/monitor_notify.go | 128 ++++ vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go | 134 ++++ vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go | 15 + vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go | 42 ++ vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go | 144 ++++ 42 files changed, 3551 insertions(+), 14 deletions(-) create mode 100644 vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/account_apikey.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/account_setting.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/account_team.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/account_user.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/account_warning.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/data_feed.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/data_source.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/apikey.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/permissions.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/settings.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/team.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/user.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/warning.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/feed.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/meta.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/region.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/source.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/answer.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/record.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/zone.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/filter.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/config.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/doc.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/job.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/notify.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_job.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_notify.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go create mode 100644 vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index bab652b..7790700 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -244,6 +244,11 @@ "Comment": "v0.3.1-102-gf5d538c", "Rev": "f5d538caab6dc0c167d4e32990c79bbf9eff578c" }, + { + "ImportPath": "github.com/xenolf/lego/providers/dns/ns1", + "Comment": "v0.3.1-102-gf5d538c", + "Rev": "f5d538caab6dc0c167d4e32990c79bbf9eff578c" + }, { "ImportPath": "github.com/xenolf/lego/providers/dns/ovh", "Comment": "v0.3.1-102-gf5d538c", @@ -276,6 +281,36 @@ "Comment": "v1.24.0", "Rev": "e3c2d47c61e5333f9aa2974695dd94396eb69c75" }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest/model/account", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest/model/data", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest/model/dns", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest/model/filter", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, + { + "ImportPath": "gopkg.in/ns1/ns1-go.v2/rest/model/monitor", + "Comment": "v1-79-gd8d10b7", + "Rev": "d8d10b7f448291ddbdce48d4594fb1b667014c8b" + }, { "ImportPath": "gopkg.in/square/go-jose.v1", "Comment": "v1.1.0", diff --git a/context.go b/context.go index 3a979fc..75bc7e5 100644 --- a/context.go +++ b/context.go @@ -94,6 +94,7 @@ func (c *Context) InitContext() { OvhApplicationSecret: getEnvOption("OVH_APPLICATION_SECRET", false), OvhConsumerKey: getEnvOption("OVH_CONSUMER_KEY", false), GandiApiKey: getEnvOption("GANDI_API_KEY", false), + NS1ApiKey: getEnvOption("NS1_API_KEY", false), } c.Acme, err = letsencrypt.NewClient(emailParam, keyType, apiVersion, providerOpts) diff --git a/letsencrypt/providers.go b/letsencrypt/providers.go index 08c06e1..68bff8a 100644 --- a/letsencrypt/providers.go +++ b/letsencrypt/providers.go @@ -11,6 +11,7 @@ import ( "github.com/xenolf/lego/providers/dns/dnsimple" "github.com/xenolf/lego/providers/dns/dyn" "github.com/xenolf/lego/providers/dns/gandi" + "github.com/xenolf/lego/providers/dns/ns1" "github.com/xenolf/lego/providers/dns/ovh" "github.com/xenolf/lego/providers/dns/route53" "github.com/xenolf/lego/providers/dns/vultr" @@ -21,6 +22,10 @@ import ( type ProviderOpts struct { Provider Provider + // AWS Route 53 credentials + AwsAccessKey string + AwsSecretKey string + // Azure credentials AzureClientId string AzureClientSecret string @@ -35,10 +40,6 @@ type ProviderOpts struct { // DigitalOcean credentials DoAccessToken string - // AWS Route 53 credentials - AwsAccessKey string - AwsSecretKey string - // DNSimple credentials DNSimpleEmail string DNSimpleKey string @@ -48,16 +49,19 @@ type ProviderOpts struct { DynUserName string DynPassword string - // Vultr credentials - VultrApiKey string + // Gandi credentials + GandiApiKey string + + // NS1 credentials + NS1ApiKey string // OVH credentials OvhApplicationKey string OvhApplicationSecret string OvhConsumerKey string - // Gandi credentials - GandiApiKey string + // Vultr credentials + VultrApiKey string } type Provider string @@ -66,12 +70,13 @@ const ( AZURE = Provider("Azure") CLOUDFLARE = Provider("CloudFlare") DIGITALOCEAN = Provider("DigitalOcean") - ROUTE53 = Provider("Route53") DNSIMPLE = Provider("DNSimple") DYN = Provider("Dyn") - VULTR = Provider("Vultr") - OVH = Provider("Ovh") GANDI = Provider("Gandi") + NS1 = Provider("NS1") + OVH = Provider("Ovh") + ROUTE53 = Provider("Route53") + VULTR = Provider("Vultr") HTTP = Provider("HTTP") ) @@ -84,12 +89,13 @@ var providerFactory = map[Provider]ProviderFactory{ AZURE: ProviderFactory{makeAzureProvider, lego.DNS01}, CLOUDFLARE: ProviderFactory{makeCloudflareProvider, lego.DNS01}, DIGITALOCEAN: ProviderFactory{makeDigitalOceanProvider, lego.DNS01}, - ROUTE53: ProviderFactory{makeRoute53Provider, lego.DNS01}, DNSIMPLE: ProviderFactory{makeDNSimpleProvider, lego.DNS01}, DYN: ProviderFactory{makeDynProvider, lego.DNS01}, - VULTR: ProviderFactory{makeVultrProvider, lego.DNS01}, - OVH: ProviderFactory{makeOvhProvider, lego.DNS01}, GANDI: ProviderFactory{makeGandiProvider, lego.DNS01}, + NS1: ProviderFactory{makeNS1Provider, lego.DNS01}, + OVH: ProviderFactory{makeOvhProvider, lego.DNS01}, + ROUTE53: ProviderFactory{makeRoute53Provider, lego.DNS01}, + VULTR: ProviderFactory{makeVultrProvider, lego.DNS01}, HTTP: ProviderFactory{makeHTTPProvider, lego.HTTP01}, } @@ -267,3 +273,16 @@ func makeAzureProvider(opts ProviderOpts) (lego.ChallengeProvider, error) { } return provider, nil } + +// returns a preconfigured NS1 lego.ChallengeProvider +func makeNS1Provider(opts ProviderOpts) (lego.ChallengeProvider, error) { + if len(opts.NS1ApiKey) == 0 { + return nil, fmt.Errorf("NS1 API key is not set") + } + + provider, err := ns1.NewDNSProviderCredentials(opts.NS1ApiKey) + if err != nil { + return nil, err + } + return provider, nil +} diff --git a/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go new file mode 100644 index 0000000..105d73f --- /dev/null +++ b/vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go @@ -0,0 +1,97 @@ +// Package ns1 implements a DNS provider for solving the DNS-01 challenge +// using NS1 DNS. +package ns1 + +import ( + "fmt" + "net/http" + "os" + "time" + + "github.com/xenolf/lego/acme" + "gopkg.in/ns1/ns1-go.v2/rest" + "gopkg.in/ns1/ns1-go.v2/rest/model/dns" +) + +// DNSProvider is an implementation of the acme.ChallengeProvider interface. +type DNSProvider struct { + client *rest.Client +} + +// NewDNSProvider returns a DNSProvider instance configured for NS1. +// Credentials must be passed in the environment variables: NS1_API_KEY. +func NewDNSProvider() (*DNSProvider, error) { + key := os.Getenv("NS1_API_KEY") + if key == "" { + return nil, fmt.Errorf("NS1 credentials missing") + } + return NewDNSProviderCredentials(key) +} + +// NewDNSProviderCredentials uses the supplied credentials to return a +// DNSProvider instance configured for NS1. +func NewDNSProviderCredentials(key string) (*DNSProvider, error) { + if key == "" { + return nil, fmt.Errorf("NS1 credentials missing") + } + + httpClient := &http.Client{Timeout: time.Second * 10} + client := rest.NewClient(httpClient, rest.SetAPIKey(key)) + + return &DNSProvider{client}, nil +} + +// Present creates a TXT record to fulfil the dns-01 challenge. +func (c *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) + + zone, err := c.getHostedZone(domain) + if err != nil { + return err + } + + record := c.newTxtRecord(zone, fqdn, value, ttl) + _, err = c.client.Records.Create(record) + if err != nil && err != rest.ErrRecordExists { + return err + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _, _ := acme.DNS01Record(domain, keyAuth) + + zone, err := c.getHostedZone(domain) + if err != nil { + return err + } + + name := acme.UnFqdn(fqdn) + _, err = c.client.Records.Delete(zone.Zone, name, "TXT") + return err +} + +func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) { + zone, _, err := c.client.Zones.Get(domain) + if err != nil { + return nil, err + } + + return zone, nil +} + +func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record { + name := acme.UnFqdn(fqdn) + + return &dns.Record{ + Type: "TXT", + Zone: zone.Zone, + Domain: name, + TTL: ttl, + Answers: []*dns.Answer{ + {Rdata: []string{value}}, + }, + } +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt b/vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt new file mode 100644 index 0000000..7b69c62 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/LICENSE.txt @@ -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): + * 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 + * . + * + * 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 + +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. + +==================================================================== diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_apikey.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_apikey.go new file mode 100644 index 0000000..9985c93 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_apikey.go @@ -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.") +) diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_setting.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_setting.go new file mode 100644 index 0000000..6211f13 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_setting.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_team.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_team.go new file mode 100644 index 0000000..8f49b06 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_team.go @@ -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.") +) diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_user.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_user.go new file mode 100644 index 0000000..a3d154b --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_user.go @@ -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.") +) diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_warning.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_warning.go new file mode 100644 index 0000000..78d4588 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/account_warning.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go new file mode 100644 index 0000000..866d038 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/client.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_feed.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_feed.go new file mode 100644 index 0000000..8c7a686 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_feed.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_source.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_source.go new file mode 100644 index 0000000..6c36ede --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/data_source.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go new file mode 100644 index 0000000..72e8ace --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/doc.go @@ -0,0 +1,2 @@ +// Package rest defines the api services used to communicate with NS1. +package rest diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/apikey.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/apikey.go new file mode 100644 index 0000000..3341207 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/apikey.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/doc.go new file mode 100644 index 0000000..1a4006d --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/doc.go @@ -0,0 +1,2 @@ +// Package account contains definitions for NS1 apikeys/teams/users/etc. +package account diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/permissions.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/permissions.go new file mode 100644 index 0000000..b733935 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/permissions.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/settings.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/settings.go new file mode 100644 index 0000000..a46ee4b --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/settings.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/team.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/team.go new file mode 100644 index 0000000..6cde798 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/team.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/user.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/user.go new file mode 100644 index 0000000..51ccc16 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/user.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/warning.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/warning.go new file mode 100644 index 0000000..52078af --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/account/warning.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/doc.go new file mode 100644 index 0000000..b3ebe5c --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/doc.go @@ -0,0 +1,2 @@ +// Package data contains definitions for NS1 metadata/sources/feeds/etc. +package data diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/feed.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/feed.go new file mode 100644 index 0000000..d481d9c --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/feed.go @@ -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} +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/meta.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/meta.go new file mode 100644 index 0000000..8638c1c --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/meta.go @@ -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"` +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/region.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/region.go new file mode 100644 index 0000000..1cc1670 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/region.go @@ -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 diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/source.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/source.go new file mode 100644 index 0000000..abe44d7 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/data/source.go @@ -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{}, + } +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/answer.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/answer.go new file mode 100644 index 0000000..a436aec --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/answer.go @@ -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, + }, + } +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/doc.go new file mode 100644 index 0000000..2853bd5 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/doc.go @@ -0,0 +1,2 @@ +// Package dns contains definitions for NS1 zones/records/answers/etc. +package dns diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/record.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/record.go new file mode 100644 index 0000000..71ca0e0 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/record.go @@ -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) +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/zone.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/zone.go new file mode 100644 index 0000000..18abe0d --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/dns/zone.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/doc.go new file mode 100644 index 0000000..73353f3 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/doc.go @@ -0,0 +1,2 @@ +// Package filter contains definitions for NS1 filter chains. +package filter diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/filter.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/filter.go new file mode 100644 index 0000000..9ed977f --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/filter/filter.go @@ -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{}} +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/config.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/config.go new file mode 100644 index 0000000..dc58b20 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/config.go @@ -0,0 +1,4 @@ +package monitor + +// Config is a flat mapping where values are simple (no slices/maps). +type Config map[string]interface{} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/doc.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/doc.go new file mode 100644 index 0000000..4021404 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/doc.go @@ -0,0 +1,2 @@ +// Package monitor contains definitions for NS1 monitoring jobs. +package monitor diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/job.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/job.go new file mode 100644 index 0000000..6943f9c --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/job.go @@ -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, + } +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/notify.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/notify.go new file mode 100644 index 0000000..4a217f7 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/model/monitor/notify.go @@ -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}} +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_job.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_job.go new file mode 100644 index 0000000..a58cab1 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_job.go @@ -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 +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_notify.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_notify.go new file mode 100644 index 0000000..e86ce21 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/monitor_notify.go @@ -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.") +) diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go new file mode 100644 index 0000000..a3e2e77 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/record.go @@ -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.") +) diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go new file mode 100644 index 0000000..5caf368 --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/stat.go @@ -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 +// } diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go new file mode 100644 index 0000000..b23c18e --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/util.go @@ -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) + }) + } +} diff --git a/vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go b/vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go new file mode 100644 index 0000000..e16aede --- /dev/null +++ b/vendor/gopkg.in/ns1/ns1-go.v2/rest/zone.go @@ -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.") +)