Merge pull request #6449 from hashicorp/b-cloudflare-apex

providers/cloudflare: record can manage apex records
This commit is contained in:
Mitchell Hashimoto 2016-05-03 13:27:43 -07:00
commit d5af110055
25 changed files with 2500 additions and 135 deletions

9
Godeps/Godeps.json generated
View File

@ -925,6 +925,10 @@
"ImportPath": "github.com/mitchellh/cli",
"Rev": "cb6853d606ea4a12a15ac83cc43503df99fd28fb"
},
{
"ImportPath": "github.com/mitchellh/cloudflare-go",
"Rev": "84c7a0993a06d555dbfddd2b32f5fa9b92fa1dc1"
},
{
"ImportPath": "github.com/mitchellh/colorstring",
"Rev": "8631ce90f28644f54aeedcb3e389a85174e067d1"
@ -999,6 +1003,11 @@
"ImportPath": "github.com/pearkes/mailgun",
"Rev": "b88605989c4141d22a6d874f78800399e5bb7ac2"
},
{
"ImportPath": "github.com/pkg/errors",
"Comment": "v0.3.0",
"Rev": "42fa80f2ac6ed17a977ce826074bd3009593fa9d"
},
{
"ImportPath": "github.com/rackspace/gophercloud",
"Comment": "v1.0.0-884-gc54bbac",

View File

@ -3,7 +3,8 @@ package cloudflare
import (
"log"
"github.com/crackcomm/cloudflare"
// NOTE: Temporary until they merge my PR:
"github.com/mitchellh/cloudflare-go"
)
type Config struct {
@ -12,13 +13,8 @@ type Config struct {
}
// Client() returns a new client for accessing cloudflare.
func (c *Config) Client() (*cloudflare.Client, error) {
client := cloudflare.New(&cloudflare.Options{
Email: c.Email,
Key: c.Token,
})
func (c *Config) Client() (*cloudflare.API, error) {
client := cloudflare.New(c.Token, c.Email)
log.Printf("[INFO] CloudFlare Client configured for user: %s", c.Email)
return client, nil
}

View File

@ -3,13 +3,11 @@ package cloudflare
import (
"fmt"
"log"
"strings"
"time"
"golang.org/x/net/context"
"github.com/crackcomm/cloudflare"
"github.com/hashicorp/terraform/helper/schema"
// NOTE: Temporary until they merge my PR:
"github.com/mitchellh/cloudflare-go"
)
func resourceCloudFlareRecord() *schema.Resource {
@ -72,13 +70,13 @@ func resourceCloudFlareRecord() *schema.Resource {
}
func resourceCloudFlareRecordCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.Client)
client := meta.(*cloudflare.API)
newRecord := &cloudflare.Record{
Content: d.Get("value").(string),
Name: d.Get("name").(string),
Proxied: d.Get("proxied").(bool),
newRecord := cloudflare.DNSRecord{
Type: d.Get("type").(string),
Name: d.Get("name").(string),
Content: d.Get("value").(string),
Proxied: d.Get("proxied").(bool),
ZoneName: d.Get("domain").(string),
}
@ -90,24 +88,22 @@ func resourceCloudFlareRecordCreate(d *schema.ResourceData, meta interface{}) er
newRecord.TTL = ttl.(int)
}
zone, err := retrieveZone(client, newRecord.ZoneName)
zoneId, err := client.ZoneIDByName(newRecord.ZoneName)
if err != nil {
return err
return fmt.Errorf("Error finding zone %q: %s", newRecord.ZoneName, err)
}
d.Set("zone_id", zone.ID)
newRecord.ZoneID = zone.ID
d.Set("zone_id", zoneId)
newRecord.ZoneID = zoneId
log.Printf("[DEBUG] CloudFlare Record create configuration: %#v", newRecord)
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
err = client.Records.Create(ctx, newRecord)
r, err := client.CreateDNSRecord(zoneId, newRecord)
if err != nil {
return fmt.Errorf("Failed to create record: %s", err)
}
d.SetId(newRecord.ID)
d.SetId(r.ID)
log.Printf("[INFO] CloudFlare Record ID: %s", d.Id())
@ -115,18 +111,15 @@ func resourceCloudFlareRecordCreate(d *schema.ResourceData, meta interface{}) er
}
func resourceCloudFlareRecordRead(d *schema.ResourceData, meta interface{}) error {
var (
client = meta.(*cloudflare.Client)
domain = d.Get("domain").(string)
rName = strings.Join([]string{d.Get("name").(string), domain}, ".")
)
client := meta.(*cloudflare.API)
domain := d.Get("domain").(string)
zone, err := retrieveZone(client, domain)
zoneId, err := client.ZoneIDByName(domain)
if err != nil {
return err
return fmt.Errorf("Error finding zone %q: %s", domain, err)
}
record, err := retrieveRecord(client, zone, rName)
record, err := client.DNSRecord(zoneId, d.Id())
if err != nil {
return err
}
@ -138,21 +131,21 @@ func resourceCloudFlareRecordRead(d *schema.ResourceData, meta interface{}) erro
d.Set("ttl", record.TTL)
d.Set("priority", record.Priority)
d.Set("proxied", record.Proxied)
d.Set("zone_id", zone.ID)
d.Set("zone_id", zoneId)
return nil
}
func resourceCloudFlareRecordUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*cloudflare.Client)
client := meta.(*cloudflare.API)
updateRecord := &cloudflare.Record{
Content: d.Get("value").(string),
updateRecord := cloudflare.DNSRecord{
ID: d.Id(),
Name: d.Get("name").(string),
Proxied: false,
Type: d.Get("type").(string),
Name: d.Get("name").(string),
Content: d.Get("value").(string),
ZoneName: d.Get("domain").(string),
Proxied: false,
}
if priority, ok := d.GetOk("priority"); ok {
@ -167,18 +160,15 @@ func resourceCloudFlareRecordUpdate(d *schema.ResourceData, meta interface{}) er
updateRecord.TTL = ttl.(int)
}
zone, err := retrieveZone(client, updateRecord.ZoneName)
zoneId, err := client.ZoneIDByName(updateRecord.ZoneName)
if err != nil {
return err
return fmt.Errorf("Error finding zone %q: %s", updateRecord.ZoneName, err)
}
updateRecord.ZoneID = zone.ID
updateRecord.ZoneID = zoneId
log.Printf("[DEBUG] CloudFlare Record update configuration: %#v", updateRecord)
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
err = client.Records.Patch(ctx, updateRecord)
err = client.UpdateDNSRecord(zoneId, d.Id(), updateRecord)
if err != nil {
return fmt.Errorf("Failed to update CloudFlare Record: %s", err)
}
@ -187,79 +177,20 @@ func resourceCloudFlareRecordUpdate(d *schema.ResourceData, meta interface{}) er
}
func resourceCloudFlareRecordDelete(d *schema.ResourceData, meta interface{}) error {
var (
client = meta.(*cloudflare.Client)
domain = d.Get("domain").(string)
rName = strings.Join([]string{d.Get("name").(string), domain}, ".")
)
client := meta.(*cloudflare.API)
domain := d.Get("domain").(string)
zone, err := retrieveZone(client, domain)
zoneId, err := client.ZoneIDByName(domain)
if err != nil {
return err
}
record, err := retrieveRecord(client, zone, rName)
if err != nil {
return err
return fmt.Errorf("Error finding zone %q: %s", domain, err)
}
log.Printf("[INFO] Deleting CloudFlare Record: %s, %s", domain, d.Id())
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
err = client.Records.Delete(ctx, zone.ID, record.ID)
err = client.DeleteDNSRecord(zoneId, d.Id())
if err != nil {
return fmt.Errorf("Error deleting CloudFlare Record: %s", err)
}
return nil
}
func retrieveRecord(
client *cloudflare.Client,
zone *cloudflare.Zone,
name string,
) (*cloudflare.Record, error) {
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
rs, err := client.Records.List(ctx, zone.ID)
if err != nil {
return nil, fmt.Errorf("Unable to retrieve records for (%s): %s", zone.Name, err)
}
var record *cloudflare.Record
for _, r := range rs {
if r.Name == name {
record = r
}
}
if record == nil {
return nil, fmt.Errorf("Unable to find Cloudflare record %s", name)
}
return record, nil
}
func retrieveZone(client *cloudflare.Client, domain string) (*cloudflare.Zone, error) {
ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
zs, err := client.Zones.List(ctx)
if err != nil {
return nil, fmt.Errorf("Failed to fetch zone for %s: %s", domain, err)
}
var zone *cloudflare.Zone
for _, z := range zs {
if z.Name == domain {
zone = z
}
}
if zone == nil {
return nil, fmt.Errorf("Failed to find zone for: %s", domain)
}
return zone, nil
}

View File

@ -4,17 +4,16 @@ import (
"fmt"
"os"
"testing"
"time"
"golang.org/x/net/context"
"github.com/crackcomm/cloudflare"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
// NOTE: Temporary until they merge my PR:
"github.com/mitchellh/cloudflare-go"
)
func TestAccCloudFlareRecord_Basic(t *testing.T) {
var record cloudflare.Record
var record cloudflare.DNSRecord
domain := os.Getenv("CLOUDFLARE_DOMAIN")
resource.Test(t, resource.TestCase{
@ -39,8 +38,34 @@ func TestAccCloudFlareRecord_Basic(t *testing.T) {
})
}
func TestAccCloudFlareRecord_Apex(t *testing.T) {
var record cloudflare.DNSRecord
domain := os.Getenv("CLOUDFLARE_DOMAIN")
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckCloudFlareRecordDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: fmt.Sprintf(testAccCheckCloudFlareRecordConfigApex, domain),
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudFlareRecordExists("cloudflare_record.foobar", &record),
testAccCheckCloudFlareRecordAttributes(&record),
resource.TestCheckResourceAttr(
"cloudflare_record.foobar", "name", "@"),
resource.TestCheckResourceAttr(
"cloudflare_record.foobar", "domain", domain),
resource.TestCheckResourceAttr(
"cloudflare_record.foobar", "value", "192.168.0.10"),
),
},
},
})
}
func TestAccCloudFlareRecord_Proxied(t *testing.T) {
var record cloudflare.Record
var record cloudflare.DNSRecord
domain := os.Getenv("CLOUDFLARE_DOMAIN")
resource.Test(t, resource.TestCase{
@ -69,7 +94,7 @@ func TestAccCloudFlareRecord_Proxied(t *testing.T) {
}
func TestAccCloudFlareRecord_Updated(t *testing.T) {
var record cloudflare.Record
var record cloudflare.DNSRecord
domain := os.Getenv("CLOUDFLARE_DOMAIN")
resource.Test(t, resource.TestCase{
@ -108,7 +133,7 @@ func TestAccCloudFlareRecord_Updated(t *testing.T) {
}
func TestAccCloudFlareRecord_forceNewRecord(t *testing.T) {
var afterCreate, afterUpdate cloudflare.Record
var afterCreate, afterUpdate cloudflare.DNSRecord
domain := os.Getenv("CLOUDFLARE_DOMAIN")
resource.Test(t, resource.TestCase{
@ -134,7 +159,7 @@ func TestAccCloudFlareRecord_forceNewRecord(t *testing.T) {
}
func testAccCheckCloudFlareRecordRecreated(t *testing.T,
before, after *cloudflare.Record) resource.TestCheckFunc {
before, after *cloudflare.DNSRecord) resource.TestCheckFunc {
return func(s *terraform.State) error {
if before.ID == after.ID {
t.Fatalf("Expected change of Record Ids, but both were %v", before.ID)
@ -144,17 +169,14 @@ func testAccCheckCloudFlareRecordRecreated(t *testing.T,
}
func testAccCheckCloudFlareRecordDestroy(s *terraform.State) error {
var (
client = testAccProvider.Meta().(*cloudflare.Client)
ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
)
client := testAccProvider.Meta().(*cloudflare.API)
for _, rs := range s.RootModule().Resources {
if rs.Type != "cloudflare_record" {
continue
}
_, err := client.Records.Details(ctx, rs.Primary.Attributes["zone_id"], rs.Primary.ID)
_, err := client.DNSRecord(rs.Primary.Attributes["zone_id"], rs.Primary.ID)
if err == nil {
return fmt.Errorf("Record still exists")
}
@ -163,7 +185,7 @@ func testAccCheckCloudFlareRecordDestroy(s *terraform.State) error {
return nil
}
func testAccCheckCloudFlareRecordAttributes(record *cloudflare.Record) resource.TestCheckFunc {
func testAccCheckCloudFlareRecordAttributes(record *cloudflare.DNSRecord) resource.TestCheckFunc {
return func(s *terraform.State) error {
if record.Content != "192.168.0.10" {
@ -174,7 +196,7 @@ func testAccCheckCloudFlareRecordAttributes(record *cloudflare.Record) resource.
}
}
func testAccCheckCloudFlareRecordAttributesUpdated(record *cloudflare.Record) resource.TestCheckFunc {
func testAccCheckCloudFlareRecordAttributesUpdated(record *cloudflare.DNSRecord) resource.TestCheckFunc {
return func(s *terraform.State) error {
if record.Content != "192.168.0.11" {
@ -185,7 +207,7 @@ func testAccCheckCloudFlareRecordAttributesUpdated(record *cloudflare.Record) re
}
}
func testAccCheckCloudFlareRecordExists(n string, record *cloudflare.Record) resource.TestCheckFunc {
func testAccCheckCloudFlareRecordExists(n string, record *cloudflare.DNSRecord) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
@ -196,12 +218,8 @@ func testAccCheckCloudFlareRecordExists(n string, record *cloudflare.Record) res
return fmt.Errorf("No Record ID is set")
}
var (
client = testAccProvider.Meta().(*cloudflare.Client)
ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Second*30))
)
foundRecord, err := client.Records.Details(ctx, rs.Primary.Attributes["zone_id"], rs.Primary.ID)
client := testAccProvider.Meta().(*cloudflare.API)
foundRecord, err := client.DNSRecord(rs.Primary.Attributes["zone_id"], rs.Primary.ID)
if err != nil {
return err
}
@ -210,7 +228,7 @@ func testAccCheckCloudFlareRecordExists(n string, record *cloudflare.Record) res
return fmt.Errorf("Record not found")
}
*record = *foundRecord
*record = foundRecord
return nil
}
@ -226,6 +244,15 @@ resource "cloudflare_record" "foobar" {
ttl = 3600
}`
const testAccCheckCloudFlareRecordConfigApex = `
resource "cloudflare_record" "foobar" {
domain = "%s"
name = "@"
value = "192.168.0.10"
type = "A"
ttl = 3600
}`
const testAccCheckCloudFlareRecordConfigProxied = `
resource "cloudflare_record" "foobar" {
domain = "%s"

23
vendor/github.com/mitchellh/cloudflare-go/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,23 @@
language: go
sudo: false
matrix:
include:
- go: 1.4
- go: 1.5
- go: 1.6
- go: tip
allow_failures:
- go: tip
script:
- go get -t -v $(go list ./... | grep -v '/vendor/')
- diff -u <(echo -n) <(gofmt -d .)
- go vet $(go list ./... | grep -v '/vendor/')
- go test -v -race ./...
notifications:
email:
recipients:
- jamesog@cloudflare.com
- msilverlock@cloudflare.com

26
vendor/github.com/mitchellh/cloudflare-go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2015-2016, CloudFlare. 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. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.

44
vendor/github.com/mitchellh/cloudflare-go/README.md generated vendored Normal file
View File

@ -0,0 +1,44 @@
[![GoDoc](https://godoc.org/github.com/cloudflare/cloudflare-go?status.svg)](https://godoc.org/github.com/cloudflare/cloudflare-go)
# cloudflare
A Go library for interacting with [CloudFlare's API v4](https://api.cloudflare.com/).
# Installation
You need a working Go environment.
```
go get github.com/cloudflare/cloudflare-go
```
# Getting Started
```
package main
import (
"fmt"
"github.com/cloudflare/cloudflare-go"
)
var api *cloudflare.API
func main() {
// Construct a new API object
api = cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
// Fetch the list of zones on the account
zones, err := api.ListZones()
if err != nil {
fmt.Println(err)
}
// Print the zone names
for _, z := range zones {
fmt.Println(z.Name)
}
}
```
An example application, [flarectl](cmd/flarectl), is in this repository.

451
vendor/github.com/mitchellh/cloudflare-go/cloudflare.go generated vendored Normal file
View File

@ -0,0 +1,451 @@
/*
Package cloudflare implements the CloudFlare v4 API.
New API requests created like:
api := cloudflare.New(apikey, apiemail)
*/
package cloudflare
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"github.com/pkg/errors"
)
const apiURL = "https://api.cloudflare.com/client/v4"
// Error messages
const errMakeRequestError = "Error from makeRequest"
const errUnmarshalError = "Error unmarshalling JSON"
type API struct {
APIKey string
APIEmail string
}
// Initializes the API configuration.
func New(key, email string) *API {
return &API{key, email}
}
// Initializes a new zone.
func NewZone() *Zone {
return &Zone{}
}
// ZoneIDByName retrieves a zone's ID from the name.
func (api *API) ZoneIDByName(zoneName string) (string, error) {
res, err := api.ListZones(zoneName)
if err != nil {
return "", errors.Wrap(err, "ListZones command failed")
}
for _, zone := range res {
if zone.Name == zoneName {
return zone.ID, nil
}
}
return "", errors.New("Zone could not be found")
}
// Params can be turned into a URL query string or a body
// TODO: Give this func a better name
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
// Replace nil with a JSON object if needed
var reqBody io.Reader
if params != nil {
json, err := json.Marshal(params)
if err != nil {
return nil, errors.Wrap(err, "Error marshalling params to JSON")
}
reqBody = bytes.NewReader(json)
} else {
reqBody = nil
}
req, err := http.NewRequest(method, apiURL+uri, reqBody)
if err != nil {
return nil, errors.Wrap(err, "HTTP request creation failed")
}
req.Header.Add("X-Auth-Key", api.APIKey)
req.Header.Add("X-Auth-Email", api.APIEmail)
// Could be application/json or multipart/form-data
// req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, errors.Wrap(err, "HTTP request failed")
}
defer resp.Body.Close()
resBody, err := ioutil.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
if err != nil {
return nil, errors.Wrap(err, "Error returned from API")
} else if resBody != nil {
return nil, errors.New(string(resBody))
} else {
return nil, errors.New(resp.Status)
}
}
return resBody, nil
}
// The Response struct is a template. There will also be a result struct.
// There will be a unique response type for each response, which will include
// this type.
type Response struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
}
type ResultInfo struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
Count int `json:"count"`
Total int `json:"total_count"`
}
// An Organization describes a multi-user organization. (Enterprise only.)
type Organization struct {
ID string
Name string
Status string
Permissions []string
Roles []string
}
// A User describes a user account.
type User struct {
ID string `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Username string `json:"username"`
Telephone string `json:"telephone"`
Country string `json:"country"`
Zipcode string `json:"zipcode"`
CreatedOn string `json:"created_on"` // Should this be a time.Date?
ModifiedOn string `json:"modified_on"`
APIKey string `json:"api_key"`
TwoFA bool `json:"two_factor_authentication_enabled"`
Betas []string `json:"betas"`
Organizations []Organization `json:"organizations"`
}
type UserResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result User `json:"result"`
}
type Owner struct {
ID string `json:"id"`
Email string `json:"email"`
OwnerType string `json:"owner_type"`
}
// A Zone describes a CloudFlare zone.
type Zone struct {
ID string `json:"id"`
Name string `json:"name"`
DevMode int `json:"development_mode"`
OriginalNS []string `json:"original_name_servers"`
OriginalRegistrar string `json:"original_registrar"`
OriginalDNSHost string `json:"original_dnshost"`
CreatedOn string `json:"created_on"`
ModifiedOn string `json:"modified_on"`
NameServers []string `json:"name_servers"`
Owner Owner `json:"owner"`
Permissions []string `json:"permissions"`
Plan ZonePlan `json:"plan"`
Status string `json:"status"`
Paused bool `json:"paused"`
Type string `json:"type"`
Host struct {
Name string
Website string
} `json:"host"`
VanityNS []string `json:"vanity_name_servers"`
Betas []string `json:"betas"`
DeactReason string `json:"deactivation_reason"`
Meta ZoneMeta `json:"meta"`
}
// Contains metadata about a zone.
type ZoneMeta struct {
// custom_certificate_quota is broken - sometimes it's a string, sometimes a number!
// CustCertQuota int `json:"custom_certificate_quota"`
PageRuleQuota int `json:"page_rule_quota"`
WildcardProxiable bool `json:"wildcard_proxiable"`
PhishingDetected bool `json:"phishing_detected"`
}
// Contains the plan information for a zone.
type ZonePlan struct {
ID string `json:"id"`
Name string `json:"name"`
Price int `json:"price"`
Currency string `json:"currency"`
Frequency string `json:"frequency"`
LegacyID string `json:"legacy_id"`
IsSubscribed bool `json:"is_subscribed"`
CanSubscribe bool `json:"can_subscribe"`
}
type ZoneResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []Zone `json:"result"`
}
type ZonePlanResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []ZonePlan `json:"result"`
}
// type zoneSetting struct {
// ID string `json:"id"`
// Editable bool `json:"editable"`
// ModifiedOn string `json:"modified_on"`
// }
// type zoneSettingStringVal struct {
// zoneSetting
// Value string `json:"value"`
// }
// type zoneSettingIntVal struct {
// zoneSetting
// Value int64 `json:"value"`
// }
type ZoneSetting struct {
ID string `json:"id"`
Editable bool `json:"editable"`
ModifiedOn string `json:"modified_on"`
Value interface{} `json:"value"`
TimeRemaining int `json:"time_remaining"`
}
type ZoneSettingResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []ZoneSetting `json:"result"`
}
// Describes a DNS record for a zone.
type DNSRecord struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Content string `json:"content,omitempty"`
Proxiable bool `json:"proxiable,omitempty"`
Proxied bool `json:"proxied,omitempty"`
TTL int `json:"ttl,omitempty"`
Locked bool `json:"locked,omitempty"`
ZoneID string `json:"zone_id,omitempty"`
ZoneName string `json:"zone_name,omitempty"`
CreatedOn string `json:"created_on,omitempty"`
ModifiedOn string `json:"modified_on,omitempty"`
Data interface{} `json:"data,omitempty"` // data returned by: SRV, LOC
Meta interface{} `json:"meta,omitempty"`
Priority int `json:"priority,omitempty"`
}
// The response for creating or updating a DNS record.
type DNSRecordResponse struct {
Success bool `json:"success"`
Errors []interface{} `json:"errors"`
Messages []string `json:"messages"`
Result DNSRecord `json:"result"`
}
// The response for listing DNS records.
type DNSListResponse struct {
Success bool `json:"success"`
Errors []interface{} `json:"errors"`
Messages []string `json:"messages"`
Result []DNSRecord `json:"result"`
}
// Railgun status for a zone.
type ZoneRailgun struct {
ID string `json:"id"`
Name string `json:"string"`
Enabled bool `json:"enabled"`
Connected bool `json:"connected"`
}
type ZoneRailgunResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []ZoneRailgun `json:"result"`
}
// Custom SSL certificates for a zone.
type ZoneCustomSSL struct {
ID string `json:"id"`
Hosts []string `json:"hosts"`
Issuer string `json:"issuer"`
Priority int `json:"priority"`
Status string `json:"success"`
BundleMethod string `json:"bundle_method"`
ZoneID string `json:"zone_id"`
Permissions []string `json:"permissions"`
UploadedOn string `json:"uploaded_on"`
ModifiedOn string `json:"modified_on"`
ExpiresOn string `json:"expires_on"`
KeylessServer KeylessSSL `json:"keyless_server"`
}
type ZoneCustomSSLResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []ZoneCustomSSL `json:"result"`
}
type KeylessSSL struct {
ID string `json:"id"`
Name string `json:"name"`
Host string `json:"host"`
Port int `json:"port"`
Status string `json:"success"`
Enabled bool `json:"enabled"`
Permissions []string `json:"permissions"`
CreatedOn string `json:"created_on"`
ModifiedOn string `json:"modifed_on"`
}
type KeylessSSLResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []KeylessSSL `json:"result"`
}
type Railgun struct {
ID string `json:"id"`
Name string `json:"name"`
Status string `json:"success"`
Enabled bool `json:"enabled"`
ZonesConnected int `json:"zones_connected"`
Build string `json:"build"`
Version string `json:"version"`
Revision string `json:"revision"`
ActivationKey string `json:"activation_key"`
ActivatedOn string `json:"activated_on"`
CreatedOn string `json:"created_on"`
ModifiedOn string `json:"modified_on"`
// XXX: UpgradeInfo struct {
// version string
// url string
// } `json:"upgrade_info"`
}
type RailgunResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []Railgun `json:"result"`
}
// Custom error pages.
type CustomPage struct {
CreatedOn string `json:"created_on"`
ModifiedOn string `json:"modified_on"`
URL string `json:"url"`
State string `json:"state"`
RequiredTokens []string `json:"required_tokens"`
PreviewTarget string `json:"preview_target"`
Description string `json:"description"`
}
type CustomPageResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []CustomPage `json:"result"`
}
// WAF packages
type WAFPackage struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
ZoneID string `json:"zone_id"`
DetectionMode string `json:"detection_mode"`
Sensitivity string `json:"sensitivity"`
ActionMode string `json:"action_mode"`
}
type WAFPackagesResponse struct {
Result []WAFPackage `json:"result"`
Success bool `json:"success"`
ResultInfo struct {
Page uint `json:"page"`
PerPage uint `json:"per_page"`
Count uint `json:"count"`
TotalCount uint `json:"total_count"`
} `json:"result_info"`
}
type WAFRule struct {
ID string `json:"id"`
Description string `json:"description"`
Priority string `json:"priority"`
PackageID string `json:"package_id"`
Group struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"group"`
Mode string `json:"mode"`
DefaultMode string `json:"default_mode"`
AllowedModes []string `json:"allowed_modes"`
}
type WAFRulesResponse struct {
Result []WAFRule `json:"result"`
Success bool `json:"success"`
ResultInfo struct {
Page uint `json:"page"`
PerPage uint `json:"per_page"`
Count uint `json:"count"`
TotalCount uint `json:"total_count"`
} `json:"result_info"`
}
type PurgeCacheRequest struct {
Everything bool `json:"purge_everything,omitempty"`
Files []string `json:"files,omitempty"`
Tags []string `json:"tags,omitempty"`
}
type PurgeCacheResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
}
// IPs contains a list of IPv4 and IPv6 CIDRs
type IPRanges struct {
IPv4CIDRs []string `json:"ipv4_cidrs"`
IPv6CIDRs []string `json:"ipv6_cidrs"`
}
// IPsResponse is the API response containing a list of IPs
type IPsResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result IPRanges `json:"result"`
}

View File

@ -0,0 +1,34 @@
# flarectl
A CLI application for interacting with a CloudFlare account.
# Usage
You must set your API key and account email address in the environment variables `CF_API_KEY` and `CF_API_EMAIL`.
```
$ export CF_API_KEY=abcdef1234567890
$ export CF_API_EMAIL=someone@example.com
$ flarectl
NAME:
flarectl - CloudFlare CLI
USAGE:
flarectl [global options] command [command options] [arguments...]
VERSION:
2015.12.0
COMMANDS:
user, u User information
zone, z Zone information
dns, d DNS records
railgun, r Railgun information
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
```

View File

@ -0,0 +1,726 @@
package main
import (
"errors"
"fmt"
"os"
"reflect"
"strings"
"github.com/cloudflare/cloudflare-go"
"github.com/codegangsta/cli"
)
var api *cloudflare.API
// Map type used for printing a table
type table map[string]string
// Print a nicely-formatted table
func makeTable(zones []table, cols ...string) {
// Store the maximum length of all columns
// The default is the length of the title
lens := make(map[string]int)
for _, col := range cols {
lens[col] = len(col)
}
// Increase the size of the column if it is larger than the current value
for _, z := range zones {
for col, val := range z {
if _, ok := lens[col]; ok && len(val) > lens[col] {
lens[col] = len(val)
}
}
}
// Print the headings and an underline for each heading
for _, col := range cols {
fmt.Printf("%s%s ", strings.Title(col), strings.Repeat(" ", lens[col]-len(col)))
}
fmt.Println()
for _, col := range cols {
fmt.Printf("%s ", strings.Repeat("-", lens[col]))
}
fmt.Println()
// And finally print the table data
for _, z := range zones {
for _, col := range cols {
fmt.Printf("%s%s ", z[col], strings.Repeat(" ", lens[col]-len(z[col])))
}
fmt.Println()
}
}
func checkEnv() error {
if api.APIKey == "" {
return errors.New("API key not defined")
}
if api.APIEmail == "" {
return errors.New("API email not defined")
}
return nil
}
// Utility function to check if CLI flags were given.
func checkFlags(c *cli.Context, flags ...string) error {
for _, flag := range flags {
if c.String(flag) == "" {
cli.ShowSubcommandHelp(c)
return fmt.Errorf("%s not specified", flag)
}
}
return nil
}
func ips(*cli.Context) {
ips, _ := cloudflare.IPs()
fmt.Println("IPv4 ranges:")
for _, r := range ips.IPv4CIDRs {
fmt.Println(" ", r)
}
fmt.Println()
fmt.Println("IPv6 ranges:")
for _, r := range ips.IPv6CIDRs {
fmt.Println(" ", r)
}
}
func userInfo(*cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
user, err := api.UserDetails()
if err != nil {
fmt.Println(err)
return
}
var output []table
output = append(output, table{
"ID": user.ID,
"Email": user.Email,
"Username": user.Username,
"Name": user.FirstName + " " + user.LastName,
"2FA": fmt.Sprintf("%t", user.TwoFA),
})
makeTable(output, "ID", "Email", "Username", "Name", "2FA")
}
func userUpdate(*cli.Context) {
}
func zoneList(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
zones, err := api.ListZones()
if err != nil {
fmt.Println(err)
return
}
var output []table
for _, z := range zones {
output = append(output, table{
"ID": z.ID,
"Name": z.Name,
"Plan": z.Plan.LegacyID,
"Status": z.Status,
})
}
makeTable(output, "ID", "Name", "Plan", "Status")
}
func zoneInfo(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
var zone string
if len(c.Args()) > 0 {
zone = c.Args()[0]
} else if c.String("zone") != "" {
zone = c.String("zone")
} else {
cli.ShowSubcommandHelp(c)
return
}
zones, err := api.ListZones(zone)
if err != nil {
fmt.Println(err)
return
}
var output []table
for _, z := range zones {
output = append(output, table{
"ID": z.ID,
"Zone": z.Name,
"Plan": z.Plan.LegacyID,
"Status": z.Status,
"Name Servers": strings.Join(z.NameServers, ", "),
"Paused": fmt.Sprintf("%t", z.Paused),
"Type": z.Type,
})
}
makeTable(output, "ID", "Zone", "Plan", "Status", "Name Servers", "Paused", "Type")
}
func zonePlan(*cli.Context) {
}
func zoneSettings(*cli.Context) {
}
func zoneRecords(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
var zone string
if len(c.Args()) > 0 {
zone = c.Args()[0]
} else if c.String("zone") != "" {
zone = c.String("zone")
} else {
cli.ShowSubcommandHelp(c)
return
}
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
// Create a an empty record for searching for records
rr := cloudflare.DNSRecord{}
var records []cloudflare.DNSRecord
if c.String("id") != "" {
rec, err := api.DNSRecord(zoneID, c.String("id"))
if err != nil {
fmt.Println(err)
return
}
records = append(records, rec)
} else {
if c.String("name") != "" {
rr.Name = c.String("name")
}
if c.String("content") != "" {
rr.Name = c.String("content")
}
var err error
records, err = api.DNSRecords(zoneID, rr)
if err != nil {
fmt.Println(err)
return
}
}
var output []table
for _, r := range records {
switch r.Type {
case "MX":
r.Content = fmt.Sprintf("%d %s", r.Priority, r.Content)
case "SRV":
dp := reflect.ValueOf(r.Data).Interface().(map[string]interface{})
r.Content = fmt.Sprintf("%.f %s", dp["priority"], r.Content)
// CloudFlare's API, annoyingly, automatically prepends the weight
// and port into content, separated by tabs.
// XXX: File this as a bug. LOC doesn't do this.
r.Content = strings.Replace(r.Content, "\t", " ", -1)
}
output = append(output, table{
"ID": r.ID,
"Type": r.Type,
"Name": r.Name,
"Content": r.Content,
"Proxied": fmt.Sprintf("%t", r.Proxied),
"TTL": fmt.Sprintf("%d", r.TTL),
})
}
makeTable(output, "ID", "Type", "Name", "Content", "Proxied", "TTL")
}
func dnsCreate(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
return
}
zone := c.String("zone")
name := c.String("name")
rtype := c.String("type")
content := c.String("content")
ttl := c.Int("ttl")
proxy := c.Bool("proxy")
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
record := cloudflare.DNSRecord{
Name: name,
Type: strings.ToUpper(rtype),
Content: content,
TTL: ttl,
Proxied: proxy,
}
err = api.CreateDNSRecord(zoneID, record)
if err != nil {
fmt.Println("Error creating DNS record:", err)
}
}
func dnsCreateOrUpdate(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
return
}
zone := c.String("zone")
name := c.String("name")
rtype := strings.ToUpper(c.String("type"))
content := c.String("content")
ttl := c.Int("ttl")
proxy := c.Bool("proxy")
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
// Look for an existing record
rr := cloudflare.DNSRecord{
Name: name + "." + zone,
}
records, err := api.DNSRecords(zoneID, rr)
if err != nil {
fmt.Println(err)
return
}
if len(records) > 0 {
// Record exists - find the ID and update it.
// This is imprecise without knowing the original content; if a label
// has multiple RRs we'll just update the first one.
for _, r := range records {
if r.Type == rtype {
rr.ID = r.ID
rr.Type = r.Type
rr.Content = content
rr.TTL = ttl
rr.Proxied = proxy
err := api.UpdateDNSRecord(zoneID, r.ID, rr)
if err != nil {
fmt.Println("Error updating DNS record:", err)
}
}
}
} else {
// Record doesn't exist - create it
rr.Type = rtype
rr.Content = content
rr.TTL = ttl
rr.Proxied = proxy
err := api.CreateDNSRecord(zoneID, rr)
if err != nil {
fmt.Println("Error creating DNS record:", err)
}
}
}
func dnsUpdate(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
if err := checkFlags(c, "zone", "id"); err != nil {
return
}
zone := c.String("zone")
recordID := c.String("id")
content := c.String("content")
ttl := c.Int("ttl")
proxy := c.Bool("proxy")
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
record := cloudflare.DNSRecord{
ID: recordID,
Content: content,
TTL: ttl,
Proxied: proxy,
}
err = api.UpdateDNSRecord(zoneID, recordID, record)
if err != nil {
fmt.Println("Error updating DNS record:", err)
}
}
func dnsDelete(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
if err := checkFlags(c, "zone", "id"); err != nil {
return
}
zone := c.String("zone")
recordID := c.String("id")
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
err = api.DeleteDNSRecord(zoneID, recordID)
if err != nil {
fmt.Println("Error deleting DNS record:", err)
}
}
func zoneCerts(*cli.Context) {
}
func zoneKeyless(*cli.Context) {
}
func zoneRailgun(*cli.Context) {
}
func pageRules(c *cli.Context) {
if err := checkEnv(); err != nil {
fmt.Println(err)
return
}
if err := checkFlags(c, "zone"); err != nil {
return
}
zone := c.String("zone")
zoneID, err := api.ZoneIDByName(zone)
if err != nil {
fmt.Println(err)
return
}
rules, err := api.ListPageRules(zoneID)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%3s %-32s %-8s %s\n", "Pri", "ID", "Status", "URL")
for _, r := range rules {
var settings []string
fmt.Printf("%3d %s %-8s %s\n", r.Priority, r.ID, r.Status, r.Targets[0].Constraint.Value)
for _, a := range r.Actions {
v := reflect.ValueOf(a.Value)
var s string
switch a.Value.(type) {
case int:
s = fmt.Sprintf("%s: %d", cloudflare.PageRuleActions[a.ID], v.Int())
case float64:
s = fmt.Sprintf("%s: %.f", cloudflare.PageRuleActions[a.ID], v.Float())
case map[string]interface{}:
vmap := a.Value.(map[string]interface{})
s = fmt.Sprintf("%s: %.f - %s", cloudflare.PageRuleActions[a.ID], vmap["status_code"], vmap["url"])
case nil:
s = fmt.Sprintf("%s", cloudflare.PageRuleActions[a.ID])
default:
s = fmt.Sprintf("%s: %s", cloudflare.PageRuleActions[a.ID], strings.Title(strings.Replace(v.String(), "_", " ", -1)))
}
settings = append(settings, s)
}
fmt.Println(" ", strings.Join(settings, ", "))
}
}
func railgun(*cli.Context) {
}
func main() {
api = cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
app := cli.NewApp()
app.Name = "flarectl"
app.Usage = "CloudFlare CLI"
app.Version = "2016.4.0"
app.Commands = []cli.Command{
{
Name: "ips",
Aliases: []string{"i"},
Action: ips,
Usage: "Print CloudFlare IP ranges",
},
{
Name: "user",
Aliases: []string{"u"},
Usage: "User information",
Subcommands: []cli.Command{
{
Name: "info",
Aliases: []string{"i"},
Action: userInfo,
Usage: "User details",
},
{
Name: "update",
Aliases: []string{"u"},
Action: userUpdate,
Usage: "Update user details",
},
},
},
{
Name: "zone",
Aliases: []string{"z"},
Usage: "Zone information",
Subcommands: []cli.Command{
{
Name: "list",
Aliases: []string{"l"},
Action: zoneList,
Usage: "List all zones on an account",
},
{
Name: "info",
Aliases: []string{"i"},
Action: zoneInfo,
Usage: "Information on one zone",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
},
},
{
Name: "plan",
Aliases: []string{"p"},
Action: zonePlan,
Usage: "Plan information for one zone",
},
{
Name: "settings",
Aliases: []string{"s"},
Action: zoneSettings,
Usage: "Settings for one zone",
},
{
Name: "dns",
Aliases: []string{"d"},
Action: zoneRecords,
Usage: "DNS records for a zone",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
},
},
{
Name: "railgun",
Aliases: []string{"r"},
Action: zoneRailgun,
Usage: "Railguns for a zone",
},
{
Name: "certs",
Aliases: []string{"c"},
Action: zoneCerts,
Usage: "Custom SSL certificates for a zone",
},
{
Name: "keyless",
Aliases: []string{"k"},
Action: zoneKeyless,
Usage: "Keyless SSL for a zone",
},
},
},
{
Name: "dns",
Aliases: []string{"d"},
Usage: "DNS records",
Subcommands: []cli.Command{
{
Name: "list",
Aliases: []string{"l"},
Action: zoneRecords,
Usage: "List DNS records for a zone",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "record id",
},
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
cli.StringFlag{
Name: "name",
Usage: "record name",
},
cli.StringFlag{
Name: "content",
Usage: "record content",
},
},
},
{
Name: "create",
Aliases: []string{"c"},
Action: dnsCreate,
Usage: "Create a DNS record",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
cli.StringFlag{
Name: "name",
Usage: "record name",
},
cli.StringFlag{
Name: "type",
Usage: "record type",
},
cli.StringFlag{
Name: "content",
Usage: "record content",
},
cli.IntFlag{
Name: "ttl",
Usage: "TTL (1 = automatic)",
Value: 1,
},
cli.BoolFlag{
Name: "proxy",
Usage: "proxy through CloudFlare (orange cloud)",
},
},
},
{
Name: "update",
Aliases: []string{"u"},
Action: dnsUpdate,
Usage: "Update a DNS record",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
cli.StringFlag{
Name: "id",
Usage: "record id",
},
cli.StringFlag{
Name: "content",
Usage: "record content",
},
cli.IntFlag{
Name: "ttl",
Usage: "TTL (1 = automatic)",
Value: 1,
},
cli.BoolFlag{
Name: "proxy",
Usage: "proxy through CloudFlare (orange cloud)",
},
},
},
{
Name: "create-or-update",
Aliases: []string{"o"},
Action: dnsCreateOrUpdate,
Usage: "Create a DNS record, or update if it exists",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
cli.StringFlag{
Name: "name",
Usage: "record name",
},
cli.StringFlag{
Name: "content",
Usage: "record content",
},
cli.StringFlag{
Name: "type",
Usage: "record type",
},
cli.IntFlag{
Name: "ttl",
Usage: "TTL (1 = automatic)",
Value: 1,
},
cli.BoolFlag{
Name: "proxy",
Usage: "proxy through CloudFlare (orange cloud)",
},
},
},
{
Name: "delete",
Aliases: []string{"d"},
Action: dnsDelete,
Usage: "Delete a DNS record",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
cli.StringFlag{
Name: "id",
Usage: "record id",
},
},
},
},
},
{
Name: "pagerules",
Aliases: []string{"p"},
Usage: "Page Rules",
Subcommands: []cli.Command{
{
Name: "list",
Aliases: []string{"l"},
Action: pageRules,
Usage: "List Page Rules for a zone",
Flags: []cli.Flag{
cli.StringFlag{
Name: "zone",
Usage: "zone name",
},
},
},
},
},
{
Name: "railgun",
Aliases: []string{"r"},
Usage: "Railgun information",
Action: railgun,
},
}
app.Run(os.Args)
}

10
vendor/github.com/mitchellh/cloudflare-go/cpage.go generated vendored Normal file
View File

@ -0,0 +1,10 @@
package cloudflare
// https://api.cloudflare.com/#custom-pages-for-a-zone-available-custom-pages
// GET /zones/:zone_identifier/custom_pages
// https://api.cloudflare.com/#custom-pages-for-a-zone-custom-page-details
// GET /zones/:zone_identifier/custom_pages/:identifier
// https://api.cloudflare.com/#custom-pages-for-a-zone-update-custom-page-url
// PUT /zones/:zone_identifier/custom_pages/:identifier

134
vendor/github.com/mitchellh/cloudflare-go/dns.go generated vendored Normal file
View File

@ -0,0 +1,134 @@
package cloudflare
import (
"encoding/json"
"net/url"
"github.com/pkg/errors"
)
/*
Create a DNS record.
API reference:
https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
POST /zones/:zone_identifier/dns_records
*/
func (api *API) CreateDNSRecord(zoneID string, rr DNSRecord) (DNSRecord, error) {
uri := "/zones/" + zoneID + "/dns_records"
res, err := api.makeRequest("POST", uri, rr)
if err != nil {
return DNSRecord{}, errors.Wrap(err, errMakeRequestError)
}
var r DNSRecordResponse
err = json.Unmarshal(res, &r)
if err != nil {
return DNSRecord{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
Fetches DNS records for a zone.
API reference:
https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
GET /zones/:zone_identifier/dns_records
*/
func (api *API) DNSRecords(zoneID string, rr DNSRecord) ([]DNSRecord, error) {
// Construct a query string
v := url.Values{}
if rr.Name != "" {
v.Set("name", rr.Name)
}
if rr.Type != "" {
v.Set("type", rr.Type)
}
if rr.Content != "" {
v.Set("content", rr.Content)
}
var query string
if len(v) > 0 {
query = "?" + v.Encode()
}
uri := "/zones/" + zoneID + "/dns_records" + query
res, err := api.makeRequest("GET", uri, nil)
if err != nil {
return []DNSRecord{}, errors.Wrap(err, errMakeRequestError)
}
var r DNSListResponse
err = json.Unmarshal(res, &r)
if err != nil {
return []DNSRecord{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
Fetches a single DNS record.
API reference:
https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
GET /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DNSRecord(zoneID, recordID string) (DNSRecord, error) {
uri := "/zones/" + zoneID + "/dns_records/" + recordID
res, err := api.makeRequest("GET", uri, nil)
if err != nil {
return DNSRecord{}, errors.Wrap(err, errMakeRequestError)
}
var r DNSRecordResponse
err = json.Unmarshal(res, &r)
if err != nil {
return DNSRecord{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
Change a DNS record.
API reference:
https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
PUT /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) UpdateDNSRecord(zoneID, recordID string, rr DNSRecord) error {
rec, err := api.DNSRecord(zoneID, recordID)
if err != nil {
return err
}
rr.Name = rec.Name
rr.Type = rec.Type
uri := "/zones/" + zoneID + "/dns_records/" + recordID
res, err := api.makeRequest("PUT", uri, rr)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r DNSRecordResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}
/*
Delete a DNS record.
API reference:
https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record
DELETE /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DeleteDNSRecord(zoneID, recordID string) error {
uri := "/zones/" + zoneID + "/dns_records/" + recordID
res, err := api.makeRequest("DELETE", uri, nil)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r DNSRecordResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}

36
vendor/github.com/mitchellh/cloudflare-go/ips.go generated vendored Normal file
View File

@ -0,0 +1,36 @@
package cloudflare
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/pkg/errors"
)
/*
IPs gets a list of CloudFlare's IP ranges
This does not require logging in to the API.
API reference:
https://api.cloudflare.com/#cloudflare-ips
GET /client/v4/ips
*/
func IPs() (IPRanges, error) {
resp, err := http.Get(apiURL + "/ips")
if err != nil {
return IPRanges{}, errors.Wrap(err, "HTTP request failed")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return IPRanges{}, errors.Wrap(err, "Response body could not be read")
}
var r IPsResponse
err = json.Unmarshal(body, &r)
if err != nil {
return IPRanges{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}

26
vendor/github.com/mitchellh/cloudflare-go/keyless.go generated vendored Normal file
View File

@ -0,0 +1,26 @@
package cloudflare
// https://api.cloudflare.com/#keyless-ssl-for-a-zone-create-a-keyless-ssl-configuration
// POST /zones/:zone_identifier/keyless_certificates
func (c *API) CreateKeyless() {
}
// https://api.cloudflare.com/#keyless-ssl-for-a-zone-list-keyless-ssls
// GET /zones/:zone_identifier/keyless_certificates
func (c *API) ListKeyless() {
}
// https://api.cloudflare.com/#keyless-ssl-for-a-zone-keyless-ssl-details
// GET /zones/:zone_identifier/keyless_certificates/:identifier
func (c *API) Keyless() {
}
// https://api.cloudflare.com/#keyless-ssl-for-a-zone-update-keyless-configuration
// PATCH /zones/:zone_identifier/keyless_certificates/:identifier
func (c *API) UpdateKeyless() {
}
// https://api.cloudflare.com/#keyless-ssl-for-a-zone-delete-keyless-configuration
// DELETE /zones/:zone_identifier/keyless_certificates/:identifier
func (c *API) DeleteKeyless() {
}

231
vendor/github.com/mitchellh/cloudflare-go/pagerules.go generated vendored Normal file
View File

@ -0,0 +1,231 @@
package cloudflare
import (
"encoding/json"
"github.com/pkg/errors"
)
/*
PageRuleTarget is the target to evaluate on a request.
Currently Target must always be "url" and Operator must be "matches". Value
is the URL pattern to match against.
*/
type PageRuleTarget struct {
Target string `json:"target"`
Constraint struct {
Operator string `json:"operator"`
Value string `json:"value"`
} `json:"constraint"`
}
/*
PageRuleAction is the action to take when the target is matched.
Valid IDs are:
always_online
always_use_https
browser_cache_ttl
browser_check
cache_level
disable_apps
disable_performance
disable_security
edge_cache_ttl
email_obfuscation
forwarding_url
ip_geolocation
mirage
railgun
rocket_loader
security_level
server_side_exclude
smart_errors
ssl
waf
*/
type PageRuleAction struct {
ID string `json:"id"`
Value interface{} `json:"value"`
}
// PageRuleActions maps API action IDs to human-readable strings
var PageRuleActions = map[string]string{
"always_online": "Always Online", // Value of type string
"always_use_https": "Always Use HTTPS", // Value of type interface{}
"browser_cache_ttl": "Browser Cache TTL", // Value of type int
"browser_check": "Browser Integrity Check", // Value of type string
"cache_level": "Cache Level", // Value of type string
"disable_apps": "Disable Apps", // Value of type interface{}
"disable_performance": "Disable Performance", // Value of type interface{}
"disable_security": "Disable Security", // Value of type interface{}
"edge_cache_ttl": "Edge Cache TTL", // Value of type int
"email_obfuscation": "Email Obfuscation", // Value of type string
"forwarding_url": "Forwarding URL", // Value of type map[string]interface
"ip_geolocation": "IP Geolocation Header", // Value of type string
"mirage": "Mirage", // Value of type string
"railgun": "Railgun", // Value of type string
"rocket_loader": "Rocker Loader", // Value of type string
"security_level": "Security Level", // Value of type string
"server_side_exclude": "Server Side Excludes", // Value of type string
"smart_errors": "Smart Errors", // Value of type string
"ssl": "SSL", // Value of type string
"waf": "Web Application Firewall", // Value of type string
}
// PageRule describes a Page Rule.
type PageRule struct {
ID string `json:"id,omitempty"`
Targets []PageRuleTarget `json:"targets"`
Actions []PageRuleAction `json:"actions"`
Priority int `json:"priority"`
Status string `json:"status"` // can be: active, paused
ModifiedOn string `json:"modified_on,omitempty"`
CreatedOn string `json:"created_on,omitempty"`
}
// PageRuleDetailResponse is the API response, containing a single PageRule.
type PageRuleDetailResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result PageRule `json:"result"`
}
// PageRulesResponse is the API response, containing an array of PageRules.
type PageRulesResponse struct {
Success bool `json:"success"`
Errors []string `json:"errors"`
Messages []string `json:"messages"`
Result []PageRule `json:"result"`
}
/*
CreatePageRule creates a new Page Rule for a zone.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-create-a-page-rule
POST /zones/:zone_identifier/pagerules
*/
func (api *API) CreatePageRule(zoneID string, rule PageRule) error {
uri := "/zones/" + zoneID + "/pagerules"
res, err := api.makeRequest("POST", uri, rule)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r PageRuleDetailResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}
/*
ListPageRules returns all Page Rules for a zone.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-list-page-rules
GET /zones/:zone_identifier/pagerules
*/
func (api *API) ListPageRules(zoneID string) ([]PageRule, error) {
uri := "/zones/" + zoneID + "/pagerules"
res, err := api.makeRequest("GET", uri, nil)
if err != nil {
return []PageRule{}, errors.Wrap(err, errMakeRequestError)
}
var r PageRulesResponse
err = json.Unmarshal(res, &r)
if err != nil {
return []PageRule{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
PageRule fetches detail about one Page Rule for a zone.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-page-rule-details
GET /zones/:zone_identifier/pagerules/:identifier
*/
func (api *API) PageRule(zoneID, ruleID string) (PageRule, error) {
uri := "/zones/" + zoneID + "/pagerules/" + ruleID
res, err := api.makeRequest("GET", uri, nil)
if err != nil {
return PageRule{}, errors.Wrap(err, errMakeRequestError)
}
var r PageRuleDetailResponse
err = json.Unmarshal(res, &r)
if err != nil {
return PageRule{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
ChangePageRule lets change individual settings for a Page Rule. This is in
contrast to UpdatePageRule which replaces the entire Page Rule.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-change-a-page-rule
PATCH /zones/:zone_identifier/pagerules/:identifier
*/
func (api *API) ChangePageRule(zoneID, ruleID string, rule PageRule) error {
uri := "/zones/" + zoneID + "/pagerules/" + ruleID
res, err := api.makeRequest("PATCH", uri, rule)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r PageRuleDetailResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}
/*
UpdatePageRule lets you replace a Page Rule. This is in contrast to
ChangePageRule which lets you change individual settings.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-update-a-page-rule
PUT /zones/:zone_identifier/pagerules/:identifier
*/
func (api *API) UpdatePageRule(zoneID, ruleID string, rule PageRule) error {
uri := "/zones/" + zoneID + "/pagerules/" + ruleID
res, err := api.makeRequest("PUT", uri, nil)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r PageRuleDetailResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}
/*
DeletePageRule deletes a Page Rule for a zone.
API reference:
https://api.cloudflare.com/#page-rules-for-a-zone-delete-a-page-rule
DELETE /zones/:zone_identifier/pagerules/:identifier
*/
func (api *API) DeletePageRule(zoneID, ruleID string) error {
uri := "/zones/" + zoneID + "/pagerules/" + ruleID
res, err := api.makeRequest("DELETE", uri, nil)
if err != nil {
return errors.Wrap(err, errMakeRequestError)
}
var r PageRuleDetailResponse
err = json.Unmarshal(res, &r)
if err != nil {
return errors.Wrap(err, errUnmarshalError)
}
return nil
}

37
vendor/github.com/mitchellh/cloudflare-go/railgun.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
package cloudflare
// Railgun
// https://api.cloudflare.com/#railgun-create-railgun
// POST /railguns
func (c *API) CreateRailgun() {
}
// https://api.cloudflare.com/#railgun-railgun-details
// GET /railguns/:identifier
// https://api.cloudflare.com/#railgun-get-zones-connected-to-a-railgun
// GET /railguns/:identifier/zones
// https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
// PATCH /railguns/:identifier
// https://api.cloudflare.com/#railgun-delete-railgun
// DELETE /railguns/:identifier
// Zone railgun info
// https://api.cloudflare.com/#railguns-for-a-zone-get-available-railguns
// GET /zones/:zone_identifier/railguns
func (c *API) Railguns() {
}
// https://api.cloudflare.com/#railguns-for-a-zone-get-railgun-details
// GET /zones/:zone_identifier/railguns/:identifier
func (c *API) Railgun() {
}
// https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
// PATCH /zones/:zone_identifier/railguns/:identifier
func (c *API) ZoneRailgun(connected bool) {
}

31
vendor/github.com/mitchellh/cloudflare-go/ssl.go generated vendored Normal file
View File

@ -0,0 +1,31 @@
package cloudflare
// https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
// POST /zones/:zone_identifier/custom_certificates
func (c *API) CreateSSL() {
}
// https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
// GET /zones/:zone_identifier/custom_certificates
func (c *API) ListSSL() {
}
// https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
// GET /zones/:zone_identifier/custom_certificates/:identifier
func (c *API) SSLDetails() {
}
// https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
// PATCH /zones/:zone_identifier/custom_certificates/:identifier
func (c *API) UpdateSSL() {
}
// https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
// PUT /zones/:zone_identifier/custom_certificates/prioritize
func (c *API) ReprioSSL() {
}
// https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
// DELETE /zones/:zone_identifier/custom_certificates/:identifier
func (c *API) DeleteSSL() {
}

35
vendor/github.com/mitchellh/cloudflare-go/user.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
package cloudflare
import (
"encoding/json"
"github.com/pkg/errors"
)
/*
Information about the logged-in user.
API reference: https://api.cloudflare.com/#user-user-details
*/
func (api API) UserDetails() (User, error) {
var r UserResponse
res, err := api.makeRequest("GET", "/user", nil)
if err != nil {
return User{}, errors.Wrap(err, errMakeRequestError)
}
err = json.Unmarshal(res, &r)
if err != nil {
return User{}, errors.Wrap(err, errUnmarshalError)
}
return r.Result, nil
}
/*
Update user properties.
API reference: https://api.cloudflare.com/#user-update-user
*/
func (api API) UpdateUser() (User, error) {
// api.makeRequest("PATCH", "/user", user)
return User{}, nil
}

55
vendor/github.com/mitchellh/cloudflare-go/waf.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
package cloudflare
import (
"encoding/json"
"github.com/pkg/errors"
)
func (api *API) ListWAFPackages(zoneID string) ([]WAFPackage, error) {
var p WAFPackagesResponse
var packages []WAFPackage
var res []byte
var err error
uri := "/zones/" + zoneID + "/firewall/waf/packages"
res, err = api.makeRequest("GET", uri, nil)
if err != nil {
return []WAFPackage{}, errors.Wrap(err, errMakeRequestError)
}
err = json.Unmarshal(res, &p)
if err != nil {
return []WAFPackage{}, errors.Wrap(err, errUnmarshalError)
}
if !p.Success {
// TODO: Provide an actual error message instead of always returning nil
return []WAFPackage{}, err
}
for pi, _ := range p.Result {
packages = append(packages, p.Result[pi])
}
return packages, nil
}
func (api *API) ListWAFRules(zoneID, packageID string) ([]WAFRule, error) {
var r WAFRulesResponse
var rules []WAFRule
var res []byte
var err error
uri := "/zones/" + zoneID + "/firewall/waf/packages/" + packageID + "/rules"
res, err = api.makeRequest("GET", uri, nil)
if err != nil {
return []WAFRule{}, errors.Wrap(err, errMakeRequestError)
}
err = json.Unmarshal(res, &r)
if err != nil {
return []WAFRule{}, errors.Wrap(err, errUnmarshalError)
}
if !r.Success {
// TODO: Provide an actual error message instead of always returning nil
return []WAFRule{}, err
}
for ri, _ := range r.Result {
rules = append(rules, r.Result[ri])
}
return rules, nil
}

145
vendor/github.com/mitchellh/cloudflare-go/zone.go generated vendored Normal file
View File

@ -0,0 +1,145 @@
package cloudflare
import (
"encoding/json"
"net/url"
"github.com/pkg/errors"
)
/*
Creates a zone on an account.
API reference: https://api.cloudflare.com/#zone-create-a-zone
*/
func (api *API) CreateZone(z Zone) {
// res, err := api.makeRequest("POST", "/zones", z)
}
/*
List zones on an account. Optionally takes a list of zones to filter results.
API reference: https://api.cloudflare.com/#zone-list-zones
*/
func (api *API) ListZones(z ...string) ([]Zone, error) {
v := url.Values{}
var res []byte
var r ZoneResponse
var zones []Zone
var err error
if len(z) > 0 {
for _, zone := range z {
v.Set("name", zone)
res, err = api.makeRequest("GET", "/zones?"+v.Encode(), nil)
if err != nil {
return []Zone{}, errors.Wrap(err, errMakeRequestError)
}
err = json.Unmarshal(res, &r)
if err != nil {
return []Zone{}, errors.Wrap(err, errUnmarshalError)
}
if !r.Success {
// TODO: Provide an actual error message instead of always returning nil
return []Zone{}, err
}
for zi, _ := range r.Result {
zones = append(zones, r.Result[zi])
}
}
} else {
res, err = api.makeRequest("GET", "/zones", nil)
if err != nil {
return []Zone{}, errors.Wrap(err, errMakeRequestError)
}
err = json.Unmarshal(res, &r)
if err != nil {
return []Zone{}, errors.Wrap(err, errUnmarshalError)
}
zones = r.Result
}
return zones, nil
}
/*
Fetches information about a zone.
https://api.cloudflare.com/#zone-zone-details
GET /zones/:id
*/
func (api *API) ZoneDetails(z Zone) {
// XXX: Should we make the user get the zone ID themselves with ListZones, or do the hard work here?
// ListZones gives the same information as this endpoint anyway so perhaps this is of limited use?
// Maybe for users who already know the ID or fetched it in another call.
type result struct {
Response
Result Zone `json:"result"`
}
// If z has an ID then query for that directly, else call ListZones to
// fetch by name.
// var zone Zone
if z.ID != "" {
// res, _ := makeRequest(c, "GET", "/zones/"+z.ID, nil)
// zone = res.Result
} else {
// zones, err := ListZones(c, z.Name)
// if err != nil {
// return
// }
// Only one zone should have been returned
// zone := zones[0]
}
}
// https://api.cloudflare.com/#zone-edit-zone-properties
// PATCH /zones/:id
func EditZone() {
}
// https://api.cloudflare.com/#zone-purge-all-files
// DELETE /zones/:id/purge_cache
func (api *API) PurgeEverything(zoneID string) (PurgeCacheResponse, error) {
uri := "/zones/" + zoneID + "/purge_cache"
res, err := api.makeRequest("DELETE", uri, PurgeCacheRequest{true, nil, nil})
if err != nil {
return PurgeCacheResponse{}, errors.Wrap(err, errMakeRequestError)
}
var r PurgeCacheResponse
err = json.Unmarshal(res, &r)
if err != nil {
return PurgeCacheResponse{}, errors.Wrap(err, errUnmarshalError)
}
return r, nil
}
// https://api.cloudflare.com/#zone-purge-individual-files-by-url-and-cache-tags
// DELETE /zones/:id/purge_cache
func (api *API) PurgeCache(zoneID string, pcr PurgeCacheRequest) (PurgeCacheResponse, error) {
uri := "/zones/" + zoneID + "/purge_cache"
res, err := api.makeRequest("DELETE", uri, pcr)
if err != nil {
return PurgeCacheResponse{}, errors.Wrap(err, errMakeRequestError)
}
var r PurgeCacheResponse
err = json.Unmarshal(res, &r)
if err != nil {
return PurgeCacheResponse{}, errors.Wrap(err, errUnmarshalError)
}
return r, nil
}
// https://api.cloudflare.com/#zone-delete-a-zone
// DELETE /zones/:id
func DeleteZone() {
}
// Zone Plan
// https://api.cloudflare.com/#zone-plan-available-plans
// https://api.cloudflare.com/#zone-plan-plan-details
// Zone Settings
// https://api.cloudflare.com/#zone-settings-for-a-zone-get-all-zone-settings
// e.g.
// https://api.cloudflare.com/#zone-settings-for-a-zone-get-always-online-setting
// https://api.cloudflare.com/#zone-settings-for-a-zone-change-always-online-setting

24
vendor/github.com/pkg/errors/.gitignore generated vendored Normal file
View File

@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

10
vendor/github.com/pkg/errors/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,10 @@
language: go
go_import_path: github.com/pkg/errors
go:
- 1.4.3
- 1.5.4
- 1.6.1
- tip
script:
- go test -v ./...

24
vendor/github.com/pkg/errors/LICENSE generated vendored Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS 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 COPYRIGHT HOLDER OR 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.

52
vendor/github.com/pkg/errors/README.md generated vendored Normal file
View File

@ -0,0 +1,52 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors)
Package errors implements functions for manipulating errors.
The traditional error handling idiom in Go is roughly akin to
```
if err != nil {
return err
}
```
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
## Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
```
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}
```
In addition, `errors.Wrap` records the file and line where it was called, allowing the programmer to retrieve the path to the original error.
## Retrieving the cause of an error
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to recurse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
```
type causer interface {
Cause() error
}
```
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
```
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
```
Would you like to know more? Read the [blog post](http://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully).
## Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
Before proposing a change, please discuss your change by raising an issue.
## Licence
MIT

248
vendor/github.com/pkg/errors/errors.go generated vendored Normal file
View File

@ -0,0 +1,248 @@
// Package errors implements functions for manipulating errors.
//
// The traditional error handling idiom in Go is roughly akin to
//
// if err != nil {
// return err
// }
//
// which applied recursively up the call stack results in error reports
// without context or debugging information. The errors package allows
// programmers to add context to the failure path in their code in a way
// that does not destroy the original value of the error.
//
// Adding context to an error
//
// The errors.Wrap function returns a new error that adds context to the
// original error. For example
//
// _, err := ioutil.ReadAll(r)
// if err != nil {
// return errors.Wrap(err, "read failed")
// }
//
// In addition, errors.Wrap records the file and line where it was called,
// allowing the programmer to retrieve the path to the original error.
//
// Retrieving the cause of an error
//
// Using errors.Wrap constructs a stack of errors, adding context to the
// preceding error. Depending on the nature of the error it may be necessary
// to reverse the operation of errors.Wrap to retrieve the original error
// for inspection. Any error value which implements this interface
//
// type causer interface {
// Cause() error
// }
//
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
// the topmost error which does nor implement causer, which is assumed to be
// the original cause. For example:
//
// switch err := errors.Cause(err).(type) {
// case *MyError:
// // handle specifically
// default:
// // unknown error
// }
package errors
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"strings"
)
// location represents a program counter that
// implements the Location() method.
type location uintptr
func (l location) Location() (string, int) {
pc := uintptr(l) - 1
fn := runtime.FuncForPC(pc)
if fn == nil {
return "unknown", 0
}
file, line := fn.FileLine(pc)
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(fn.Name(), sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file, line
}
// New returns an error that formats as the given text.
func New(text string) error {
pc, _, _, _ := runtime.Caller(1)
return struct {
error
location
}{
errors.New(text),
location(pc),
}
}
type cause struct {
cause error
message string
}
func (c cause) Error() string { return c.Message() + ": " + c.Cause().Error() }
func (c cause) Cause() error { return c.cause }
func (c cause) Message() string { return c.message }
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
func Errorf(format string, args ...interface{}) error {
pc, _, _, _ := runtime.Caller(1)
return struct {
error
location
}{
fmt.Errorf(format, args...),
location(pc),
}
}
// Wrap returns an error annotating the cause with message.
// If cause is nil, Wrap returns nil.
func Wrap(cause error, message string) error {
if cause == nil {
return nil
}
pc, _, _, _ := runtime.Caller(1)
return wrap(cause, message, pc)
}
// Wrapf returns an error annotating the cause with the format specifier.
// If cause is nil, Wrapf returns nil.
func Wrapf(cause error, format string, args ...interface{}) error {
if cause == nil {
return nil
}
pc, _, _, _ := runtime.Caller(1)
return wrap(cause, fmt.Sprintf(format, args...), pc)
}
func wrap(err error, msg string, pc uintptr) error {
return struct {
cause
location
}{
cause{
cause: err,
message: msg,
},
location(pc),
}
}
type causer interface {
Cause() error
}
// Cause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type Causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func Cause(err error) error {
for err != nil {
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
return err
}
// Print prints the error to Stderr.
// If the error implements the Causer interface described in Cause
// Print will recurse into the error's cause.
// If the error implements the inteface:
//
// type Location interface {
// Location() (file string, line int)
// }
//
// Print will also print the file and line of the error.
func Print(err error) {
Fprint(os.Stderr, err)
}
// Fprint prints the error to the supplied writer.
// The format of the output is the same as Print.
// If err is nil, nothing is printed.
func Fprint(w io.Writer, err error) {
type location interface {
Location() (string, int)
}
type message interface {
Message() string
}
for err != nil {
if err, ok := err.(location); ok {
file, line := err.Location()
fmt.Fprintf(w, "%s:%d: ", file, line)
}
switch err := err.(type) {
case message:
fmt.Fprintln(w, err.Message())
default:
fmt.Fprintln(w, err.Error())
}
cause, ok := err.(causer)
if !ok {
break
}
err = cause.Cause()
}
}