forked from TrueCloudLab/lego
cloudflare: use the official go client. (#658)
This commit is contained in:
parent
8a8aa2d81b
commit
18fe57183d
36 changed files with 4592 additions and 451 deletions
17
Gopkg.lock
generated
17
Gopkg.lock
generated
|
@ -126,6 +126,14 @@
|
||||||
revision = "8b15f938ed215522a37275106e847f6f0be85fe8"
|
revision = "8b15f938ed215522a37275106e847f6f0be85fe8"
|
||||||
version = "v1.15.23"
|
version = "v1.15.23"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:03cfacdc6bfd46007c15786c1ece3fa074f89e5193a292f0f26d9e98c99c7cc2"
|
||||||
|
name = "github.com/cloudflare/cloudflare-go"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "1f9007fbecae20711133c60519338c41cef1ffb4"
|
||||||
|
version = "v0.8.5"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:2a7013a7e8cfec93dd725f95ea0c4b390d7d71f8a8bc3dbde05cfc9524ba5ea8"
|
digest = "1:2a7013a7e8cfec93dd725f95ea0c4b390d7d71f8a8bc3dbde05cfc9524ba5ea8"
|
||||||
|
@ -483,6 +491,14 @@
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
version = "v0.3.0"
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:c9e7a4b4d47c0ed205d257648b0e5b0440880cb728506e318f8ac7cd36270bc4"
|
||||||
|
name = "golang.org/x/time"
|
||||||
|
packages = ["rate"]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "fbb02b2291d28baffd63558aa44b4b56f178d650"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:90f61cd4297f7f4ce75f142744673ecdafd9023668cd24730f14033f265e394a"
|
digest = "1:90f61cd4297f7f4ce75f142744673ecdafd9023668cd24730f14033f265e394a"
|
||||||
|
@ -574,6 +590,7 @@
|
||||||
"github.com/aws/aws-sdk-go/aws/session",
|
"github.com/aws/aws-sdk-go/aws/session",
|
||||||
"github.com/aws/aws-sdk-go/service/lightsail",
|
"github.com/aws/aws-sdk-go/service/lightsail",
|
||||||
"github.com/aws/aws-sdk-go/service/route53",
|
"github.com/aws/aws-sdk-go/service/route53",
|
||||||
|
"github.com/cloudflare/cloudflare-go",
|
||||||
"github.com/cpu/goacmedns",
|
"github.com/cpu/goacmedns",
|
||||||
"github.com/decker502/dnspod-go",
|
"github.com/decker502/dnspod-go",
|
||||||
"github.com/dnsimple/dnsimple-go/dnsimple",
|
"github.com/dnsimple/dnsimple-go/dnsimple",
|
||||||
|
|
|
@ -53,7 +53,7 @@ func TestPEMCertExpiration(t *testing.T) {
|
||||||
|
|
||||||
// Some random string should return an error.
|
// Some random string should return an error.
|
||||||
ctime, err := GetPEMCertExpiration(buf.Bytes())
|
ctime, err := GetPEMCertExpiration(buf.Bytes())
|
||||||
assert.Errorf(t, err, "Expected getCertExpiration to return an error for garbage string but returned %v", ctime)
|
require.Errorf(t, err, "Expected getCertExpiration to return an error for garbage string but returned %v", ctime)
|
||||||
|
|
||||||
// A DER encoded certificate should return an error.
|
// A DER encoded certificate should return an error.
|
||||||
_, err = GetPEMCertExpiration(certBytes)
|
_, err = GetPEMCertExpiration(certBytes)
|
||||||
|
|
|
@ -1,212 +0,0 @@
|
||||||
package cloudflare
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaultBaseURL represents the API endpoint to call.
|
|
||||||
const defaultBaseURL = "https://api.cloudflare.com/client/v4"
|
|
||||||
|
|
||||||
// APIError contains error details for failed requests
|
|
||||||
type APIError struct {
|
|
||||||
Code int `json:"code,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
ErrorChain []APIError `json:"error_chain,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIResponse represents a response from Cloudflare API
|
|
||||||
type APIResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Errors []*APIError `json:"errors"`
|
|
||||||
Result json.RawMessage `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxtRecord represents a Cloudflare DNS record
|
|
||||||
type TxtRecord struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
TTL int `json:"ttl,omitempty"`
|
|
||||||
ZoneID string `json:"zone_id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HostedZone represents a Cloudflare DNS zone
|
|
||||||
type HostedZone struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client Cloudflare API client
|
|
||||||
type Client struct {
|
|
||||||
authEmail string
|
|
||||||
authKey string
|
|
||||||
BaseURL string
|
|
||||||
HTTPClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClient create a Cloudflare API client
|
|
||||||
func NewClient(authEmail string, authKey string) (*Client, error) {
|
|
||||||
if authEmail == "" {
|
|
||||||
return nil, errors.New("cloudflare: some credentials information are missing: email")
|
|
||||||
}
|
|
||||||
|
|
||||||
if authKey == "" {
|
|
||||||
return nil, errors.New("cloudflare: some credentials information are missing: key")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Client{
|
|
||||||
authEmail: authEmail,
|
|
||||||
authKey: authKey,
|
|
||||||
BaseURL: defaultBaseURL,
|
|
||||||
HTTPClient: http.DefaultClient,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHostedZoneID get hosted zone
|
|
||||||
func (c *Client) GetHostedZoneID(fqdn string) (string, error) {
|
|
||||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := c.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var hostedZone []HostedZone
|
|
||||||
err = json.Unmarshal(result, &hostedZone)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("cloudflare: HostedZone unmarshaling error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
count := len(hostedZone)
|
|
||||||
if count == 0 {
|
|
||||||
return "", fmt.Errorf("cloudflare: zone %s not found for domain %s", authZone, fqdn)
|
|
||||||
} else if count > 1 {
|
|
||||||
return "", fmt.Errorf("cloudflare: zone %s cannot be find for domain %s: too many hostedZone: %v", authZone, fqdn, hostedZone)
|
|
||||||
}
|
|
||||||
|
|
||||||
return hostedZone[0].ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindTxtRecord Find a TXT record
|
|
||||||
func (c *Client) FindTxtRecord(zoneID, fqdn string) (*TxtRecord, error) {
|
|
||||||
result, err := c.doRequest(
|
|
||||||
http.MethodGet,
|
|
||||||
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)),
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var records []TxtRecord
|
|
||||||
err = json.Unmarshal(result, &records)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cloudflare: record unmarshaling error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rec := range records {
|
|
||||||
fmt.Println(rec.Name, acme.UnFqdn(fqdn))
|
|
||||||
if rec.Name == acme.UnFqdn(fqdn) {
|
|
||||||
return &rec, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cloudflare: no existing record found for %s", fqdn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTxtRecord add a TXT record
|
|
||||||
func (c *Client) AddTxtRecord(fqdn string, record TxtRecord) error {
|
|
||||||
zoneID, err := c.GetHostedZoneID(fqdn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := json.Marshal(record)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cloudflare: record marshaling error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = c.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTxtRecord Remove a TXT record
|
|
||||||
func (c *Client) RemoveTxtRecord(fqdn string) error {
|
|
||||||
zoneID, err := c.GetHostedZoneID(fqdn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
record, err := c.FindTxtRecord(zoneID, fqdn)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = c.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) doRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", c.BaseURL, uri), body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("X-Auth-Email", c.authEmail)
|
|
||||||
req.Header.Set("X-Auth-Key", c.authKey)
|
|
||||||
|
|
||||||
resp, err := c.HTTPClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cloudflare: error querying API: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cloudflare: %s", toUnreadableBodyMessage(req, content))
|
|
||||||
}
|
|
||||||
|
|
||||||
var r APIResponse
|
|
||||||
err = json.Unmarshal(content, &r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("cloudflare: APIResponse unmarshaling error: %v: %s", err, toUnreadableBodyMessage(req, content))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !r.Success {
|
|
||||||
if len(r.Errors) > 0 {
|
|
||||||
return nil, fmt.Errorf("cloudflare: error \n%s", toError(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("cloudflare: %s", toUnreadableBodyMessage(req, content))
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.Result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toUnreadableBodyMessage(req *http.Request, rawBody []byte) string {
|
|
||||||
return fmt.Sprintf("the request %s sent a response with a body which is an invalid format: %q", req.URL, string(rawBody))
|
|
||||||
}
|
|
||||||
|
|
||||||
func toError(r APIResponse) error {
|
|
||||||
errStr := ""
|
|
||||||
for _, apiErr := range r.Errors {
|
|
||||||
errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message)
|
|
||||||
for _, chainErr := range apiErr.ErrorChain {
|
|
||||||
errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("cloudflare: error \n%s", errStr)
|
|
||||||
}
|
|
|
@ -1,192 +0,0 @@
|
||||||
package cloudflare
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handlerMock(method string, response *APIResponse, data interface{}) http.Handler {
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
if req.Method != method {
|
|
||||||
content, err := json.Marshal(APIResponse{
|
|
||||||
Success: false,
|
|
||||||
Errors: []*APIError{
|
|
||||||
{
|
|
||||||
Code: 666,
|
|
||||||
Message: fmt.Sprintf("invalid method: got %s want %s", req.Method, method),
|
|
||||||
ErrorChain: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
http.Error(rw, string(content), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonData, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
response.Result = jsonData
|
|
||||||
|
|
||||||
content, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = rw.Write(content)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_GetHostedZoneID(t *testing.T) {
|
|
||||||
type result struct {
|
|
||||||
zoneID string
|
|
||||||
error bool
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
fqdn string
|
|
||||||
response *APIResponse
|
|
||||||
data []HostedZone
|
|
||||||
expected result
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "zone found",
|
|
||||||
fqdn: "_acme-challenge.foo.com.",
|
|
||||||
response: &APIResponse{Success: true},
|
|
||||||
data: []HostedZone{
|
|
||||||
{
|
|
||||||
ID: "A",
|
|
||||||
Name: "ZONE_A",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: result{zoneID: "A"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "no many zones",
|
|
||||||
fqdn: "_acme-challenge.foo.com.",
|
|
||||||
response: &APIResponse{Success: true},
|
|
||||||
data: []HostedZone{
|
|
||||||
{
|
|
||||||
ID: "A",
|
|
||||||
Name: "ZONE_A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ID: "B",
|
|
||||||
Name: "ZONE_B",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: result{error: true},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "no zone found",
|
|
||||||
fqdn: "_acme-challenge.foo.com.",
|
|
||||||
response: &APIResponse{Success: true},
|
|
||||||
expected: result{error: true},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
server := httptest.NewServer(handlerMock(http.MethodGet, test.response, test.data))
|
|
||||||
|
|
||||||
client, _ := NewClient("authEmail", "authKey")
|
|
||||||
client.BaseURL = server.URL
|
|
||||||
|
|
||||||
zoneID, err := client.GetHostedZoneID(test.fqdn)
|
|
||||||
|
|
||||||
if test.expected.error {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, test.expected.zoneID, zoneID)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClient_FindTxtRecord(t *testing.T) {
|
|
||||||
type result struct {
|
|
||||||
txtRecord *TxtRecord
|
|
||||||
error bool
|
|
||||||
}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
fqdn string
|
|
||||||
zoneID string
|
|
||||||
response *APIResponse
|
|
||||||
data []TxtRecord
|
|
||||||
expected result
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "TXT record found",
|
|
||||||
fqdn: "_acme-challenge.foo.com.",
|
|
||||||
zoneID: "ZONE_A",
|
|
||||||
response: &APIResponse{Success: true},
|
|
||||||
data: []TxtRecord{
|
|
||||||
{
|
|
||||||
Name: "_acme-challenge.foo.com",
|
|
||||||
Type: "TXT",
|
|
||||||
Content: "txtTXTtxtTXTtxtTXTtxtTXT",
|
|
||||||
ID: "A",
|
|
||||||
TTL: 50,
|
|
||||||
ZoneID: "ZONE_A",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: result{
|
|
||||||
txtRecord: &TxtRecord{
|
|
||||||
Name: "_acme-challenge.foo.com",
|
|
||||||
Type: "TXT",
|
|
||||||
Content: "txtTXTtxtTXTtxtTXTtxtTXT",
|
|
||||||
ID: "A",
|
|
||||||
TTL: 50,
|
|
||||||
ZoneID: "ZONE_A",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "TXT record not found",
|
|
||||||
fqdn: "_acme-challenge.foo.com.",
|
|
||||||
zoneID: "ZONE_A",
|
|
||||||
response: &APIResponse{Success: true},
|
|
||||||
expected: result{error: true},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCases {
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
server := httptest.NewServer(handlerMock(http.MethodGet, test.response, test.data))
|
|
||||||
|
|
||||||
client, _ := NewClient("authEmail", "authKey")
|
|
||||||
client.BaseURL = server.URL
|
|
||||||
|
|
||||||
txtRecord, err := client.FindTxtRecord(test.zoneID, test.fqdn)
|
|
||||||
|
|
||||||
if test.expected.error {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, test.expected.txtRecord, txtRecord)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,12 +8,18 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflare-go"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
"github.com/xenolf/lego/platform/config/env"
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CloudFlareAPIURL represents the API endpoint to call.
|
// CloudFlareAPIURL represents the API endpoint to call.
|
||||||
const CloudFlareAPIURL = defaultBaseURL // Deprecated
|
const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4" // Deprecated
|
||||||
|
|
||||||
|
const (
|
||||||
|
minTTL = 120
|
||||||
|
)
|
||||||
|
|
||||||
// Config is used to configure the creation of the DNSProvider
|
// Config is used to configure the creation of the DNSProvider
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -28,7 +34,7 @@ type Config struct {
|
||||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
func NewDefaultConfig() *Config {
|
func NewDefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
TTL: env.GetOrDefaultInt("CLOUDFLARE_TTL", 120),
|
TTL: env.GetOrDefaultInt("CLOUDFLARE_TTL", minTTL),
|
||||||
PropagationTimeout: env.GetOrDefaultSecond("CLOUDFLARE_PROPAGATION_TIMEOUT", 2*time.Minute),
|
PropagationTimeout: env.GetOrDefaultSecond("CLOUDFLARE_PROPAGATION_TIMEOUT", 2*time.Minute),
|
||||||
PollingInterval: env.GetOrDefaultSecond("CLOUDFLARE_POLLING_INTERVAL", 2*time.Second),
|
PollingInterval: env.GetOrDefaultSecond("CLOUDFLARE_POLLING_INTERVAL", 2*time.Second),
|
||||||
HTTPClient: &http.Client{
|
HTTPClient: &http.Client{
|
||||||
|
@ -39,7 +45,7 @@ func NewDefaultConfig() *Config {
|
||||||
|
|
||||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
client *Client
|
client *cloudflare.API
|
||||||
config *Config
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,20 +84,19 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
return nil, errors.New("cloudflare: the configuration of the DNS provider is nil")
|
return nil, errors.New("cloudflare: the configuration of the DNS provider is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := NewClient(config.AuthEmail, config.AuthKey)
|
if config.TTL < minTTL {
|
||||||
|
config.TTL = minTTL
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := cloudflare.New(config.AuthKey, config.AuthEmail, cloudflare.HTTPClient(config.HTTPClient))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client.HTTPClient = config.HTTPClient
|
|
||||||
|
|
||||||
// TODO: must be remove. keep only for compatibility reason.
|
// TODO: must be remove. keep only for compatibility reason.
|
||||||
client.BaseURL = CloudFlareAPIURL
|
client.BaseURL = CloudFlareAPIURL
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{client: client, config: config}, nil
|
||||||
client: client,
|
|
||||||
config: config,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||||
|
@ -104,19 +109,67 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
rec := TxtRecord{
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneID, err := d.client.ZoneIDByName(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: failed to find zone %s: %v", authZone, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsRecord := cloudflare.DNSRecord{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
Name: acme.UnFqdn(fqdn),
|
Name: acme.UnFqdn(fqdn),
|
||||||
Content: value,
|
Content: value,
|
||||||
TTL: d.config.TTL,
|
TTL: d.config.TTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.client.AddTxtRecord(fqdn, rec)
|
response, _ := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: failed to create TXT record: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !response.Success {
|
||||||
|
return fmt.Errorf("cloudflare: failed to create TXT record: %+v %+v", response.Errors, response.Messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("cloudflare: new record for %s, ID %s", domain, response.Result.ID)
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
return d.client.RemoveTxtRecord(fqdn)
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneID, err := d.client.ZoneIDByName(authZone)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: failed to find zone %s: %v", authZone, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsRecord := cloudflare.DNSRecord{
|
||||||
|
Type: "TXT",
|
||||||
|
Name: acme.UnFqdn(fqdn),
|
||||||
|
}
|
||||||
|
|
||||||
|
records, err := d.client.DNSRecords(zoneID, dnsRecord)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cloudflare: failed to find TXT records: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, record := range records {
|
||||||
|
err = d.client.DeleteDNSRecord(zoneID, record.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("cloudflare: failed to delete TXT record: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -29,44 +30,116 @@ func restoreEnv() {
|
||||||
os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey)
|
os.Setenv("CLOUDFLARE_API_KEY", cflareAPIKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewDNSProviderValid(t *testing.T) {
|
func TestNewDNSProvider(t *testing.T) {
|
||||||
os.Setenv("CLOUDFLARE_EMAIL", "")
|
testCases := []struct {
|
||||||
os.Setenv("CLOUDFLARE_API_KEY", "")
|
desc string
|
||||||
defer restoreEnv()
|
envVars map[string]string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"CLOUDFLARE_EMAIL": "test@example.com",
|
||||||
|
"CLOUDFLARE_API_KEY": "123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"CLOUDFLARE_EMAIL": "",
|
||||||
|
"CLOUDFLARE_API_KEY": "",
|
||||||
|
},
|
||||||
|
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing email",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"CLOUDFLARE_EMAIL": "",
|
||||||
|
"CLOUDFLARE_API_KEY": "key",
|
||||||
|
},
|
||||||
|
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing api key",
|
||||||
|
envVars: map[string]string{
|
||||||
|
"CLOUDFLARE_EMAIL": "awesome@possum.com",
|
||||||
|
"CLOUDFLARE_API_KEY": "",
|
||||||
|
},
|
||||||
|
expected: "cloudflare: some credentials information are missing: CLOUDFLARE_API_KEY",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
config := NewDefaultConfig()
|
for _, test := range testCases {
|
||||||
config.AuthEmail = "123"
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
config.AuthKey = "123"
|
defer restoreEnv()
|
||||||
|
for key, value := range test.envVars {
|
||||||
|
if len(value) == 0 {
|
||||||
|
os.Unsetenv(key)
|
||||||
|
} else {
|
||||||
|
os.Setenv(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err := NewDNSProviderConfig(config)
|
p, err := NewDNSProvider()
|
||||||
|
|
||||||
assert.NoError(t, err)
|
if len(test.expected) == 0 {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, p)
|
||||||
|
} else {
|
||||||
|
require.EqualError(t, err, test.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewDNSProviderValidEnv(t *testing.T) {
|
func TestNewDNSProviderConfig(t *testing.T) {
|
||||||
defer restoreEnv()
|
testCases := []struct {
|
||||||
os.Setenv("CLOUDFLARE_EMAIL", "test@example.com")
|
desc string
|
||||||
os.Setenv("CLOUDFLARE_API_KEY", "123")
|
authEmail string
|
||||||
|
authKey string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "success",
|
||||||
|
authEmail: "test@example.com",
|
||||||
|
authKey: "123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing credentials",
|
||||||
|
expected: "invalid credentials: key & email must not be empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing email",
|
||||||
|
authKey: "123",
|
||||||
|
expected: "invalid credentials: key & email must not be empty",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "missing api key",
|
||||||
|
authEmail: "test@example.com",
|
||||||
|
expected: "invalid credentials: key & email must not be empty",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
_, err := NewDNSProvider()
|
for _, test := range testCases {
|
||||||
assert.NoError(t, err)
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
}
|
defer restoreEnv()
|
||||||
|
os.Unsetenv("CLOUDFLARE_EMAIL")
|
||||||
|
os.Unsetenv("CLOUDFLARE_API_KEY")
|
||||||
|
|
||||||
func TestNewDNSProviderMissingCredErr(t *testing.T) {
|
config := NewDefaultConfig()
|
||||||
defer restoreEnv()
|
config.AuthEmail = test.authEmail
|
||||||
os.Setenv("CLOUDFLARE_EMAIL", "")
|
config.AuthKey = test.authKey
|
||||||
os.Setenv("CLOUDFLARE_API_KEY", "")
|
|
||||||
|
|
||||||
_, err := NewDNSProvider()
|
p, err := NewDNSProviderConfig(config)
|
||||||
assert.EqualError(t, err, "cloudflare: some credentials information are missing: CLOUDFLARE_EMAIL,CLOUDFLARE_API_KEY")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewDNSProviderMissingCredErrSingle(t *testing.T) {
|
if len(test.expected) == 0 {
|
||||||
defer restoreEnv()
|
assert.NoError(t, err)
|
||||||
os.Setenv("CLOUDFLARE_EMAIL", "awesome@possum.com")
|
assert.NotNil(t, p)
|
||||||
|
} else {
|
||||||
_, err := NewDNSProvider()
|
require.EqualError(t, err, test.expected)
|
||||||
assert.EqualError(t, err, "cloudflare: some credentials information are missing: CLOUDFLARE_API_KEY")
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloudFlarePresent(t *testing.T) {
|
func TestCloudFlarePresent(t *testing.T) {
|
||||||
|
@ -79,10 +152,10 @@ func TestCloudFlarePresent(t *testing.T) {
|
||||||
config.AuthKey = cflareAPIKey
|
config.AuthKey = cflareAPIKey
|
||||||
|
|
||||||
provider, err := NewDNSProviderConfig(config)
|
provider, err := NewDNSProviderConfig(config)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = provider.Present(cflareDomain, "", "123d==")
|
err = provider.Present(cflareDomain, "", "123d==")
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloudFlareCleanUp(t *testing.T) {
|
func TestCloudFlareCleanUp(t *testing.T) {
|
||||||
|
@ -97,8 +170,8 @@ func TestCloudFlareCleanUp(t *testing.T) {
|
||||||
config.AuthKey = cflareAPIKey
|
config.AuthKey = cflareAPIKey
|
||||||
|
|
||||||
provider, err := NewDNSProviderConfig(config)
|
provider, err := NewDNSProviderConfig(config)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = provider.CleanUp(cflareDomain, "", "123d==")
|
err = provider.CleanUp(cflareDomain, "", "123d==")
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
26
vendor/github.com/cloudflare/cloudflare-go/LICENSE
generated
vendored
Normal file
26
vendor/github.com/cloudflare/cloudflare-go/LICENSE
generated
vendored
Normal 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.
|
318
vendor/github.com/cloudflare/cloudflare-go/cloudflare.go
generated
vendored
Normal file
318
vendor/github.com/cloudflare/cloudflare-go/cloudflare.go
generated
vendored
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
// Package cloudflare implements the Cloudflare v4 API.
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
const apiURL = "https://api.cloudflare.com/client/v4"
|
||||||
|
const (
|
||||||
|
// AuthKeyEmail specifies that we should authenticate with API key and email address
|
||||||
|
AuthKeyEmail = 1 << iota
|
||||||
|
// AuthUserService specifies that we should authenticate with a User-Service key
|
||||||
|
AuthUserService
|
||||||
|
)
|
||||||
|
|
||||||
|
// API holds the configuration for the current API client. A client should not
|
||||||
|
// be modified concurrently.
|
||||||
|
type API struct {
|
||||||
|
APIKey string
|
||||||
|
APIEmail string
|
||||||
|
APIUserServiceKey string
|
||||||
|
BaseURL string
|
||||||
|
organizationID string
|
||||||
|
headers http.Header
|
||||||
|
httpClient *http.Client
|
||||||
|
authType int
|
||||||
|
rateLimiter *rate.Limiter
|
||||||
|
retryPolicy RetryPolicy
|
||||||
|
logger Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Cloudflare v4 API client.
|
||||||
|
func New(key, email string, opts ...Option) (*API, error) {
|
||||||
|
if key == "" || email == "" {
|
||||||
|
return nil, errors.New(errEmptyCredentials)
|
||||||
|
}
|
||||||
|
|
||||||
|
silentLogger := log.New(ioutil.Discard, "", log.LstdFlags)
|
||||||
|
|
||||||
|
api := &API{
|
||||||
|
APIKey: key,
|
||||||
|
APIEmail: email,
|
||||||
|
BaseURL: apiURL,
|
||||||
|
headers: make(http.Header),
|
||||||
|
authType: AuthKeyEmail,
|
||||||
|
rateLimiter: rate.NewLimiter(rate.Limit(4), 1), // 4rps equates to default api limit (1200 req/5 min)
|
||||||
|
retryPolicy: RetryPolicy{
|
||||||
|
MaxRetries: 3,
|
||||||
|
MinRetryDelay: time.Duration(1) * time.Second,
|
||||||
|
MaxRetryDelay: time.Duration(30) * time.Second,
|
||||||
|
},
|
||||||
|
logger: silentLogger,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := api.parseOptions(opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "options parsing failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to http.DefaultClient if the package user does not provide
|
||||||
|
// their own.
|
||||||
|
if api.httpClient == nil {
|
||||||
|
api.httpClient = http.DefaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
return api, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthType sets the authentication method (AuthyKeyEmail or AuthUserService).
|
||||||
|
func (api *API) SetAuthType(authType int) {
|
||||||
|
api.authType = authType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeRequest makes a HTTP request and returns the body as a byte slice,
|
||||||
|
// closing it before returnng. params will be serialized to JSON.
|
||||||
|
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
|
||||||
|
return api.makeRequestWithAuthType(method, uri, params, api.authType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) makeRequestWithAuthType(method, uri string, params interface{}, authType int) ([]byte, error) {
|
||||||
|
// Replace nil with a JSON object if needed
|
||||||
|
var jsonBody []byte
|
||||||
|
var err error
|
||||||
|
if params != nil {
|
||||||
|
jsonBody, err = json.Marshal(params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error marshalling params to JSON")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
jsonBody = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp *http.Response
|
||||||
|
var respErr error
|
||||||
|
var reqBody io.Reader
|
||||||
|
var respBody []byte
|
||||||
|
for i := 0; i <= api.retryPolicy.MaxRetries; i++ {
|
||||||
|
if jsonBody != nil {
|
||||||
|
reqBody = bytes.NewReader(jsonBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
// expect the backoff introduced here on errored requests to dominate the effect of rate limiting
|
||||||
|
// dont need a random component here as the rate limiter should do something similar
|
||||||
|
// nb time duration could truncate an arbitrary float. Since our inputs are all ints, we should be ok
|
||||||
|
sleepDuration := time.Duration(math.Pow(2, float64(i-1)) * float64(api.retryPolicy.MinRetryDelay))
|
||||||
|
|
||||||
|
if sleepDuration > api.retryPolicy.MaxRetryDelay {
|
||||||
|
sleepDuration = api.retryPolicy.MaxRetryDelay
|
||||||
|
}
|
||||||
|
// useful to do some simple logging here, maybe introduce levels later
|
||||||
|
api.logger.Printf("Sleeping %s before retry attempt number %d for request %s %s", sleepDuration.String(), i, method, uri)
|
||||||
|
time.Sleep(sleepDuration)
|
||||||
|
}
|
||||||
|
api.rateLimiter.Wait(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Error caused by request rate limiting")
|
||||||
|
}
|
||||||
|
resp, respErr = api.request(method, uri, reqBody, authType)
|
||||||
|
|
||||||
|
// retry if the server is rate limiting us or if it failed
|
||||||
|
// assumes server operations are rolled back on failure
|
||||||
|
if respErr != nil || resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500 {
|
||||||
|
// if we got a valid http response, try to read body so we can reuse the connection
|
||||||
|
// see https://golang.org/pkg/net/http/#Client.Do
|
||||||
|
if respErr == nil {
|
||||||
|
respBody, err = ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
respErr = errors.Wrap(err, "could not read response body")
|
||||||
|
|
||||||
|
api.logger.Printf("Request: %s %s got an error response %d: %s\n", method, uri, resp.StatusCode,
|
||||||
|
strings.Replace(strings.Replace(string(respBody), "\n", "", -1), "\t", "", -1))
|
||||||
|
} else {
|
||||||
|
api.logger.Printf("Error performing request: %s %s : %s \n", method, uri, respErr.Error())
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
respBody, err = ioutil.ReadAll(resp.Body)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "could not read response body")
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if respErr != nil {
|
||||||
|
return nil, respErr
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices:
|
||||||
|
case resp.StatusCode == http.StatusUnauthorized:
|
||||||
|
return nil, errors.Errorf("HTTP status %d: invalid credentials", resp.StatusCode)
|
||||||
|
case resp.StatusCode == http.StatusForbidden:
|
||||||
|
return nil, errors.Errorf("HTTP status %d: insufficient permissions", resp.StatusCode)
|
||||||
|
case resp.StatusCode == http.StatusServiceUnavailable,
|
||||||
|
resp.StatusCode == http.StatusBadGateway,
|
||||||
|
resp.StatusCode == http.StatusGatewayTimeout,
|
||||||
|
resp.StatusCode == 522,
|
||||||
|
resp.StatusCode == 523,
|
||||||
|
resp.StatusCode == 524:
|
||||||
|
return nil, errors.Errorf("HTTP status %d: service failure", resp.StatusCode)
|
||||||
|
default:
|
||||||
|
var s string
|
||||||
|
if respBody != nil {
|
||||||
|
s = string(respBody)
|
||||||
|
}
|
||||||
|
return nil, errors.Errorf("HTTP status %d: content %q", resp.StatusCode, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respBody, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// request makes a HTTP request to the given API endpoint, returning the raw
|
||||||
|
// *http.Response, or an error if one occurred. The caller is responsible for
|
||||||
|
// closing the response body.
|
||||||
|
func (api *API) request(method, uri string, reqBody io.Reader, authType int) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(method, api.BaseURL+uri, reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "HTTP request creation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply any user-defined headers first.
|
||||||
|
req.Header = cloneHeader(api.headers)
|
||||||
|
if authType&AuthKeyEmail != 0 {
|
||||||
|
req.Header.Set("X-Auth-Key", api.APIKey)
|
||||||
|
req.Header.Set("X-Auth-Email", api.APIEmail)
|
||||||
|
}
|
||||||
|
if authType&AuthUserService != 0 {
|
||||||
|
req.Header.Set("X-Auth-User-Service-Key", api.APIUserServiceKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Header.Get("Content-Type") == "" {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := api.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "HTTP request failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the base URL to use for API endpoints that exist for both accounts and organizations.
|
||||||
|
// If an Organization option was used when creating the API instance, returns the org URL.
|
||||||
|
//
|
||||||
|
// accountBase is the base URL for endpoints referring to the current user. It exists as a
|
||||||
|
// parameter because it is not consistent across APIs.
|
||||||
|
func (api *API) userBaseURL(accountBase string) string {
|
||||||
|
if api.organizationID != "" {
|
||||||
|
return "/organizations/" + api.organizationID
|
||||||
|
}
|
||||||
|
return accountBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// cloneHeader returns a shallow copy of the header.
|
||||||
|
// copied from https://godoc.org/github.com/golang/gddo/httputil/header#Copy
|
||||||
|
func cloneHeader(header http.Header) http.Header {
|
||||||
|
h := make(http.Header)
|
||||||
|
for k, vs := range header {
|
||||||
|
h[k] = vs
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseInfo contains a code and message returned by the API as errors or
|
||||||
|
// informational messages inside the response.
|
||||||
|
type ResponseInfo struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response 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 []ResponseInfo `json:"errors"`
|
||||||
|
Messages []ResponseInfo `json:"messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResultInfo contains metadata about the Response.
|
||||||
|
type ResultInfo struct {
|
||||||
|
Page int `json:"page"`
|
||||||
|
PerPage int `json:"per_page"`
|
||||||
|
TotalPages int `json:"total_pages"`
|
||||||
|
Count int `json:"count"`
|
||||||
|
Total int `json:"total_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawResponse keeps the result as JSON form
|
||||||
|
type RawResponse struct {
|
||||||
|
Response
|
||||||
|
Result json.RawMessage `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw makes a HTTP request with user provided params and returns the
|
||||||
|
// result as untouched JSON.
|
||||||
|
func (api *API) Raw(method, endpoint string, data interface{}) (json.RawMessage, error) {
|
||||||
|
res, err := api.makeRequest(method, endpoint, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r RawResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaginationOptions can be passed to a list request to configure paging
|
||||||
|
// These values will be defaulted if omitted, and PerPage has min/max limits set by resource
|
||||||
|
type PaginationOptions struct {
|
||||||
|
Page int `json:"page,omitempty"`
|
||||||
|
PerPage int `json:"per_page,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryPolicy specifies number of retries and min/max retry delays
|
||||||
|
// This config is used when the client exponentially backs off after errored requests
|
||||||
|
type RetryPolicy struct {
|
||||||
|
MaxRetries int
|
||||||
|
MinRetryDelay time.Duration
|
||||||
|
MaxRetryDelay time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger defines the interface this library needs to use logging
|
||||||
|
// This is a subset of the methods implemented in the log package
|
||||||
|
type Logger interface {
|
||||||
|
Printf(format string, v ...interface{})
|
||||||
|
}
|
29
vendor/github.com/cloudflare/cloudflare-go/cpage.go
generated
vendored
Normal file
29
vendor/github.com/cloudflare/cloudflare-go/cpage.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// CustomPage represents a custom page configuration.
|
||||||
|
type CustomPage struct {
|
||||||
|
CreatedOn string `json:"created_on"`
|
||||||
|
ModifiedOn time.Time `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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomPageResponse represents the response from the custom pages endpoint.
|
||||||
|
type CustomPageResponse struct {
|
||||||
|
Response
|
||||||
|
Result []CustomPage `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
149
vendor/github.com/cloudflare/cloudflare-go/custom_hostname.go
generated
vendored
Normal file
149
vendor/github.com/cloudflare/cloudflare-go/custom_hostname.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomHostnameSSL represents the SSL section in a given custom hostname.
|
||||||
|
type CustomHostnameSSL struct {
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
CnameTarget string `json:"cname_target,omitempty"`
|
||||||
|
CnameName string `json:"cname_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomMetadata defines custom metadata for the hostname. This requires logic to be implemented by Cloudflare to act on the data provided.
|
||||||
|
type CustomMetadata map[string]interface{}
|
||||||
|
|
||||||
|
// CustomHostname represents a custom hostname in a zone.
|
||||||
|
type CustomHostname struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
SSL CustomHostnameSSL `json:"ssl,omitempty"`
|
||||||
|
CustomMetadata CustomMetadata `json:"custom_metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHostNameResponse represents a response from the Custom Hostnames endpoints.
|
||||||
|
type CustomHostnameResponse struct {
|
||||||
|
Result CustomHostname `json:"result"`
|
||||||
|
Response
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHostnameListResponse represents a response from the Custom Hostnames endpoints.
|
||||||
|
type CustomHostnameListResponse struct {
|
||||||
|
Result []CustomHostname `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify SSL configuration for the given custom hostname in the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-update-custom-hostname-configuration
|
||||||
|
func (api *API) UpdateCustomHostnameSSL(zoneID string, customHostnameID string, ssl CustomHostnameSSL) (CustomHostname, error) {
|
||||||
|
return CustomHostname{}, errors.New("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a custom hostname (and any issued SSL certificates)
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-delete-a-custom-hostname-and-any-issued-ssl-certificates-
|
||||||
|
func (api *API) DeleteCustomHostname(zoneID string, customHostnameID string) error {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response *CustomHostnameResponse
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCustomHostname creates a new custom hostname and requests that an SSL certificate be issued for it.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-create-custom-hostname
|
||||||
|
func (api *API) CreateCustomHostname(zoneID string, ch CustomHostname) (*CustomHostnameResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_hostnames"
|
||||||
|
res, err := api.makeRequest("POST", uri, ch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response *CustomHostnameResponse
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHostnames fetches custom hostnames for the given zone,
|
||||||
|
// by applying filter.Hostname if not empty and scoping the result to page'th 50 items.
|
||||||
|
//
|
||||||
|
// The returned ResultInfo can be used to implement pagination.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-list-custom-hostnames
|
||||||
|
func (api *API) CustomHostnames(zoneID string, page int, filter CustomHostname) ([]CustomHostname, ResultInfo, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("per_page", "50")
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
if filter.Hostname != "" {
|
||||||
|
v.Set("hostname", filter.Hostname)
|
||||||
|
}
|
||||||
|
query := "?" + v.Encode()
|
||||||
|
|
||||||
|
uri := "/zones/" + zoneID + "/custom_hostnames" + query
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []CustomHostname{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var customHostnameListResponse CustomHostnameListResponse
|
||||||
|
err = json.Unmarshal(res, &customHostnameListResponse)
|
||||||
|
if err != nil {
|
||||||
|
return []CustomHostname{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return customHostnameListResponse.Result, customHostnameListResponse.ResultInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHostname inspects the given custom hostname in the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-hostname-for-a-zone-custom-hostname-configuration-details
|
||||||
|
func (api *API) CustomHostname(zoneID string, customHostnameID string) (CustomHostname, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_hostnames/" + customHostnameID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return CustomHostname{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var response CustomHostnameResponse
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return CustomHostname{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomHostnameIDByName retrieves the ID for the given hostname in the given zone.
|
||||||
|
func (api *API) CustomHostnameIDByName(zoneID string, hostname string) (string, error) {
|
||||||
|
customHostnames, _, err := api.CustomHostnames(zoneID, 1, CustomHostname{Hostname: hostname})
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "CustomHostnames command failed")
|
||||||
|
}
|
||||||
|
for _, ch := range customHostnames {
|
||||||
|
if ch.Hostname == hostname {
|
||||||
|
return ch.ID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", errors.New("CustomHostname could not be found")
|
||||||
|
}
|
174
vendor/github.com/cloudflare/cloudflare-go/dns.go
generated
vendored
Normal file
174
vendor/github.com/cloudflare/cloudflare-go/dns.go
generated
vendored
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSRecord represents a DNS record in 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 time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn time.Time `json:"modified_on,omitempty"`
|
||||||
|
Data interface{} `json:"data,omitempty"` // data returned by: SRV, LOC
|
||||||
|
Meta interface{} `json:"meta,omitempty"`
|
||||||
|
Priority int `json:"priority,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecordResponse represents the response from the DNS endpoint.
|
||||||
|
type DNSRecordResponse struct {
|
||||||
|
Result DNSRecord `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSListResponse represents the response from the list DNS records endpoint.
|
||||||
|
type DNSListResponse struct {
|
||||||
|
Result []DNSRecord `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDNSRecord creates a DNS record for the zone identifier.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
|
||||||
|
func (api *API) CreateDNSRecord(zoneID string, rr DNSRecord) (*DNSRecordResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/dns_records"
|
||||||
|
res, err := api.makeRequest("POST", uri, rr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var recordResp *DNSRecordResponse
|
||||||
|
err = json.Unmarshal(res, &recordResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return recordResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecords returns a slice of DNS records for the given zone identifier.
|
||||||
|
//
|
||||||
|
// This takes a DNSRecord to allow filtering of the results returned.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
|
||||||
|
func (api *API) DNSRecords(zoneID string, rr DNSRecord) ([]DNSRecord, error) {
|
||||||
|
// Construct a query string
|
||||||
|
v := url.Values{}
|
||||||
|
// Request as many records as possible per page - API max is 50
|
||||||
|
v.Set("per_page", "50")
|
||||||
|
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
|
||||||
|
var records []DNSRecord
|
||||||
|
page := 1
|
||||||
|
|
||||||
|
// Loop over makeRequest until what we've fetched all records
|
||||||
|
for {
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
records = append(records, r.Result...)
|
||||||
|
if r.ResultInfo.Page >= r.ResultInfo.TotalPages {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Loop around and fetch the next page
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return records, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSRecord returns a single DNS record for the given zone & record
|
||||||
|
// identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDNSRecord updates a single DNS record for the given zone & record
|
||||||
|
// identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
|
||||||
|
func (api *API) UpdateDNSRecord(zoneID, recordID string, rr DNSRecord) error {
|
||||||
|
rec, err := api.DNSRecord(zoneID, recordID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Populate the record name from the existing one if the update didn't
|
||||||
|
// specify it.
|
||||||
|
if rr.Name == "" {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteDNSRecord deletes a single DNS record for the given zone & record
|
||||||
|
// identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record
|
||||||
|
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
|
||||||
|
}
|
48
vendor/github.com/cloudflare/cloudflare-go/errors.go
generated
vendored
Normal file
48
vendor/github.com/cloudflare/cloudflare-go/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
// Error messages
|
||||||
|
const (
|
||||||
|
errEmptyCredentials = "invalid credentials: key & email must not be empty"
|
||||||
|
errMakeRequestError = "error from makeRequest"
|
||||||
|
errUnmarshalError = "error unmarshalling the JSON response"
|
||||||
|
errRequestNotSuccessful = "error reported by API"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Error = &UserError{}
|
||||||
|
|
||||||
|
// Error represents an error returned from this library.
|
||||||
|
type Error interface {
|
||||||
|
error
|
||||||
|
// Raised when user credentials or configuration is invalid.
|
||||||
|
User() bool
|
||||||
|
// Raised when a parsing error (e.g. JSON) occurs.
|
||||||
|
Parse() bool
|
||||||
|
// Raised when a network error occurs.
|
||||||
|
Network() bool
|
||||||
|
// Contains the most recent error.
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserError represents a user-generated error.
|
||||||
|
type UserError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// User is a user-caused error.
|
||||||
|
func (e *UserError) User() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network error.
|
||||||
|
func (e *UserError) Network() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse error.
|
||||||
|
func (e *UserError) Parse() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error wraps the underlying error.
|
||||||
|
func (e *UserError) Error() string {
|
||||||
|
return e.Err.Error()
|
||||||
|
}
|
241
vendor/github.com/cloudflare/cloudflare-go/firewall.go
generated
vendored
Normal file
241
vendor/github.com/cloudflare/cloudflare-go/firewall.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessRule represents a firewall access rule.
|
||||||
|
type AccessRule struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Notes string `json:"notes,omitempty"`
|
||||||
|
AllowedModes []string `json:"allowed_modes,omitempty"`
|
||||||
|
Mode string `json:"mode,omitempty"`
|
||||||
|
Configuration AccessRuleConfiguration `json:"configuration,omitempty"`
|
||||||
|
Scope AccessRuleScope `json:"scope,omitempty"`
|
||||||
|
CreatedOn time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn time.Time `json:"modified_on,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessRuleConfiguration represents the configuration of a firewall
|
||||||
|
// access rule.
|
||||||
|
type AccessRuleConfiguration struct {
|
||||||
|
Target string `json:"target,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessRuleScope represents the scope of a firewall access rule.
|
||||||
|
type AccessRuleScope struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessRuleResponse represents the response from the firewall access
|
||||||
|
// rule endpoint.
|
||||||
|
type AccessRuleResponse struct {
|
||||||
|
Result AccessRule `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccessRuleListResponse represents the response from the list access rules
|
||||||
|
// endpoint.
|
||||||
|
type AccessRuleListResponse struct {
|
||||||
|
Result []AccessRule `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserAccessRules returns a slice of access rules for the logged-in user.
|
||||||
|
//
|
||||||
|
// This takes an AccessRule to allow filtering of the results returned.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-list-access-rules
|
||||||
|
func (api *API) ListUserAccessRules(accessRule AccessRule, page int) (*AccessRuleListResponse, error) {
|
||||||
|
return api.listAccessRules("/user", accessRule, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserAccessRule creates a firewall access rule for the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-create-access-rule
|
||||||
|
func (api *API) CreateUserAccessRule(accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.createAccessRule("/user", accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserAccessRule updates a single access rule for the logged-in user &
|
||||||
|
// given access rule identifier.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-update-access-rule
|
||||||
|
func (api *API) UpdateUserAccessRule(accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.updateAccessRule("/user", accessRuleID, accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserAccessRule deletes a single access rule for the logged-in user and
|
||||||
|
// access rule identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-level-firewall-access-rule-update-access-rule
|
||||||
|
func (api *API) DeleteUserAccessRule(accessRuleID string) (*AccessRuleResponse, error) {
|
||||||
|
return api.deleteAccessRule("/user", accessRuleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListZoneAccessRules returns a slice of access rules for the given zone
|
||||||
|
// identifier.
|
||||||
|
//
|
||||||
|
// This takes an AccessRule to allow filtering of the results returned.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-list-access-rules
|
||||||
|
func (api *API) ListZoneAccessRules(zoneID string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) {
|
||||||
|
return api.listAccessRules("/zones/"+zoneID, accessRule, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateZoneAccessRule creates a firewall access rule for the given zone
|
||||||
|
// identifier.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-create-access-rule
|
||||||
|
func (api *API) CreateZoneAccessRule(zoneID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.createAccessRule("/zones/"+zoneID, accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateZoneAccessRule updates a single access rule for the given zone &
|
||||||
|
// access rule identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-update-access-rule
|
||||||
|
func (api *API) UpdateZoneAccessRule(zoneID, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.updateAccessRule("/zones/"+zoneID, accessRuleID, accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteZoneAccessRule deletes a single access rule for the given zone and
|
||||||
|
// access rule identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#firewall-access-rule-for-a-zone-delete-access-rule
|
||||||
|
func (api *API) DeleteZoneAccessRule(zoneID, accessRuleID string) (*AccessRuleResponse, error) {
|
||||||
|
return api.deleteAccessRule("/zones/"+zoneID, accessRuleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrganizationAccessRules returns a slice of access rules for the given
|
||||||
|
// organization identifier.
|
||||||
|
//
|
||||||
|
// This takes an AccessRule to allow filtering of the results returned.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-level-firewall-access-rule-list-access-rules
|
||||||
|
func (api *API) ListOrganizationAccessRules(organizationID string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) {
|
||||||
|
return api.listAccessRules("/organizations/"+organizationID, accessRule, page)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOrganizationAccessRule creates a firewall access rule for the given
|
||||||
|
// organization identifier.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-level-firewall-access-rule-create-access-rule
|
||||||
|
func (api *API) CreateOrganizationAccessRule(organizationID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.createAccessRule("/organizations/"+organizationID, accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOrganizationAccessRule updates a single access rule for the given
|
||||||
|
// organization & access rule identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-level-firewall-access-rule-update-access-rule
|
||||||
|
func (api *API) UpdateOrganizationAccessRule(organizationID, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
return api.updateAccessRule("/organizations/"+organizationID, accessRuleID, accessRule)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOrganizationAccessRule deletes a single access rule for the given
|
||||||
|
// organization and access rule identifiers.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-level-firewall-access-rule-delete-access-rule
|
||||||
|
func (api *API) DeleteOrganizationAccessRule(organizationID, accessRuleID string) (*AccessRuleResponse, error) {
|
||||||
|
return api.deleteAccessRule("/organizations/"+organizationID, accessRuleID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) listAccessRules(prefix string, accessRule AccessRule, page int) (*AccessRuleListResponse, error) {
|
||||||
|
// Construct a query string
|
||||||
|
v := url.Values{}
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
// Request as many rules as possible per page - API max is 100
|
||||||
|
v.Set("per_page", "100")
|
||||||
|
if accessRule.Notes != "" {
|
||||||
|
v.Set("notes", accessRule.Notes)
|
||||||
|
}
|
||||||
|
if accessRule.Mode != "" {
|
||||||
|
v.Set("mode", accessRule.Mode)
|
||||||
|
}
|
||||||
|
if accessRule.Scope.Type != "" {
|
||||||
|
v.Set("scope_type", accessRule.Scope.Type)
|
||||||
|
}
|
||||||
|
if accessRule.Configuration.Value != "" {
|
||||||
|
v.Set("configuration_value", accessRule.Configuration.Value)
|
||||||
|
}
|
||||||
|
if accessRule.Configuration.Target != "" {
|
||||||
|
v.Set("configuration_target", accessRule.Configuration.Target)
|
||||||
|
}
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
query := "?" + v.Encode()
|
||||||
|
|
||||||
|
uri := prefix + "/firewall/access_rules/rules" + query
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &AccessRuleListResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) createAccessRule(prefix string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
uri := prefix + "/firewall/access_rules/rules"
|
||||||
|
res, err := api.makeRequest("POST", uri, accessRule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &AccessRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) updateAccessRule(prefix, accessRuleID string, accessRule AccessRule) (*AccessRuleResponse, error) {
|
||||||
|
uri := prefix + "/firewall/access_rules/rules/" + accessRuleID
|
||||||
|
res, err := api.makeRequest("PATCH", uri, accessRule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &AccessRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) deleteAccessRule(prefix, accessRuleID string) (*AccessRuleResponse, error) {
|
||||||
|
uri := prefix + "/firewall/access_rules/rules/" + accessRuleID
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &AccessRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
44
vendor/github.com/cloudflare/cloudflare-go/ips.go
generated
vendored
Normal file
44
vendor/github.com/cloudflare/cloudflare-go/ips.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPRanges contains lists 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 {
|
||||||
|
Response
|
||||||
|
Result IPRanges `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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
|
||||||
|
}
|
52
vendor/github.com/cloudflare/cloudflare-go/keyless.go
generated
vendored
Normal file
52
vendor/github.com/cloudflare/cloudflare-go/keyless.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// KeylessSSL represents Keyless SSL configuration.
|
||||||
|
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 time.Time `json:"created_on"`
|
||||||
|
ModifiedOn time.Time `json:"modifed_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeylessSSLResponse represents the response from the Keyless SSL endpoint.
|
||||||
|
type KeylessSSLResponse struct {
|
||||||
|
Response
|
||||||
|
Result []KeylessSSL `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateKeyless creates a new Keyless SSL configuration for the zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-create-a-keyless-ssl-configuration
|
||||||
|
func (api *API) CreateKeyless() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListKeyless lists Keyless SSL configurations for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-list-keyless-ssls
|
||||||
|
func (api *API) ListKeyless() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keyless provides the configuration for a given Keyless SSL identifier.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-keyless-ssl-details
|
||||||
|
func (api *API) Keyless() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateKeyless updates an existing Keyless SSL configuration.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-update-keyless-configuration
|
||||||
|
func (api *API) UpdateKeyless() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteKeyless deletes an existing Keyless SSL configuration.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#keyless-ssl-for-a-zone-delete-keyless-configuration
|
||||||
|
func (api *API) DeleteKeyless() {
|
||||||
|
}
|
330
vendor/github.com/cloudflare/cloudflare-go/load_balancing.go
generated
vendored
Normal file
330
vendor/github.com/cloudflare/cloudflare-go/load_balancing.go
generated
vendored
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadBalancerPool represents a load balancer pool's properties.
|
||||||
|
type LoadBalancerPool struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
CreatedOn *time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn *time.Time `json:"modified_on,omitempty"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
MinimumOrigins int `json:"minimum_origins,omitempty"`
|
||||||
|
Monitor string `json:"monitor,omitempty"`
|
||||||
|
Origins []LoadBalancerOrigin `json:"origins"`
|
||||||
|
NotificationEmail string `json:"notification_email,omitempty"`
|
||||||
|
|
||||||
|
// CheckRegions defines the geographic region(s) from where to run health-checks from - e.g. "WNAM", "WEU", "SAF", "SAM".
|
||||||
|
// Providing a null/empty value means "all regions", which may not be available to all plan types.
|
||||||
|
CheckRegions []string `json:"check_regions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalancerOrigin struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Weight float64 `json:"weight"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancerMonitor represents a load balancer monitor's properties.
|
||||||
|
type LoadBalancerMonitor struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
CreatedOn *time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn *time.Time `json:"modified_on,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Header map[string][]string `json:"header"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Retries int `json:"retries"`
|
||||||
|
Interval int `json:"interval"`
|
||||||
|
ExpectedBody string `json:"expected_body"`
|
||||||
|
ExpectedCodes string `json:"expected_codes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancer represents a load balancer's properties.
|
||||||
|
type LoadBalancer struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
CreatedOn *time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn *time.Time `json:"modified_on,omitempty"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TTL int `json:"ttl,omitempty"`
|
||||||
|
FallbackPool string `json:"fallback_pool"`
|
||||||
|
DefaultPools []string `json:"default_pools"`
|
||||||
|
RegionPools map[string][]string `json:"region_pools"`
|
||||||
|
PopPools map[string][]string `json:"pop_pools"`
|
||||||
|
Proxied bool `json:"proxied"`
|
||||||
|
Persistence string `json:"session_affinity,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerPoolResponse represents the response from the load balancer pool endpoints.
|
||||||
|
type loadBalancerPoolResponse struct {
|
||||||
|
Response
|
||||||
|
Result LoadBalancerPool `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerPoolListResponse represents the response from the List Pools endpoint.
|
||||||
|
type loadBalancerPoolListResponse struct {
|
||||||
|
Response
|
||||||
|
Result []LoadBalancerPool `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerMonitorResponse represents the response from the load balancer monitor endpoints.
|
||||||
|
type loadBalancerMonitorResponse struct {
|
||||||
|
Response
|
||||||
|
Result LoadBalancerMonitor `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerMonitorListResponse represents the response from the List Monitors endpoint.
|
||||||
|
type loadBalancerMonitorListResponse struct {
|
||||||
|
Response
|
||||||
|
Result []LoadBalancerMonitor `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerResponse represents the response from the load balancer endpoints.
|
||||||
|
type loadBalancerResponse struct {
|
||||||
|
Response
|
||||||
|
Result LoadBalancer `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadBalancerListResponse represents the response from the List Load Balancers endpoint.
|
||||||
|
type loadBalancerListResponse struct {
|
||||||
|
Response
|
||||||
|
Result []LoadBalancer `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLoadBalancerPool creates a new load balancer pool.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-pools-create-a-pool
|
||||||
|
func (api *API) CreateLoadBalancerPool(pool LoadBalancerPool) (LoadBalancerPool, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/pools"
|
||||||
|
res, err := api.makeRequest("POST", uri, pool)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerPoolResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListLoadBalancerPools lists load balancer pools connected to an account.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-pools-list-pools
|
||||||
|
func (api *API) ListLoadBalancerPools() ([]LoadBalancerPool, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/pools"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerPoolListResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancerPoolDetails returns the details for a load balancer pool.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-pools-pool-details
|
||||||
|
func (api *API) LoadBalancerPoolDetails(poolID string) (LoadBalancerPool, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/pools/" + poolID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerPoolResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLoadBalancerPool disables and deletes a load balancer pool.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-pools-delete-a-pool
|
||||||
|
func (api *API) DeleteLoadBalancerPool(poolID string) error {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/pools/" + poolID
|
||||||
|
if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyLoadBalancerPool modifies a configured load balancer pool.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-pools-modify-a-pool
|
||||||
|
func (api *API) ModifyLoadBalancerPool(pool LoadBalancerPool) (LoadBalancerPool, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/pools/" + pool.ID
|
||||||
|
res, err := api.makeRequest("PUT", uri, pool)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerPoolResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerPool{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLoadBalancerMonitor creates a new load balancer monitor.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-monitors-create-a-monitor
|
||||||
|
func (api *API) CreateLoadBalancerMonitor(monitor LoadBalancerMonitor) (LoadBalancerMonitor, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/monitors"
|
||||||
|
res, err := api.makeRequest("POST", uri, monitor)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerMonitorResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListLoadBalancerMonitors lists load balancer monitors connected to an account.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-monitors-list-monitors
|
||||||
|
func (api *API) ListLoadBalancerMonitors() ([]LoadBalancerMonitor, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/monitors"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerMonitorListResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancerMonitorDetails returns the details for a load balancer monitor.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-monitors-monitor-details
|
||||||
|
func (api *API) LoadBalancerMonitorDetails(monitorID string) (LoadBalancerMonitor, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitorID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerMonitorResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLoadBalancerMonitor disables and deletes a load balancer monitor.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-monitors-delete-a-monitor
|
||||||
|
func (api *API) DeleteLoadBalancerMonitor(monitorID string) error {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitorID
|
||||||
|
if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyLoadBalancerMonitor modifies a configured load balancer monitor.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancer-monitors-modify-a-monitor
|
||||||
|
func (api *API) ModifyLoadBalancerMonitor(monitor LoadBalancerMonitor) (LoadBalancerMonitor, error) {
|
||||||
|
uri := api.userBaseURL("/user") + "/load_balancers/monitors/" + monitor.ID
|
||||||
|
res, err := api.makeRequest("PUT", uri, monitor)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerMonitorResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancerMonitor{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateLoadBalancer creates a new load balancer.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancers-create-a-load-balancer
|
||||||
|
func (api *API) CreateLoadBalancer(zoneID string, lb LoadBalancer) (LoadBalancer, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/load_balancers"
|
||||||
|
res, err := api.makeRequest("POST", uri, lb)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListLoadBalancers lists load balancers configured on a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancers-list-load-balancers
|
||||||
|
func (api *API) ListLoadBalancers(zoneID string) ([]LoadBalancer, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/load_balancers"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerListResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBalancerDetails returns the details for a load balancer.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancers-load-balancer-details
|
||||||
|
func (api *API) LoadBalancerDetails(zoneID, lbID string) (LoadBalancer, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/load_balancers/" + lbID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLoadBalancer disables and deletes a load balancer.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancers-delete-a-load-balancer
|
||||||
|
func (api *API) DeleteLoadBalancer(zoneID, lbID string) error {
|
||||||
|
uri := "/zones/" + zoneID + "/load_balancers/" + lbID
|
||||||
|
if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyLoadBalancer modifies a configured load balancer.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#load-balancers-modify-a-load-balancer
|
||||||
|
func (api *API) ModifyLoadBalancer(zoneID string, lb LoadBalancer) (LoadBalancer, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/load_balancers/" + lb.ID
|
||||||
|
res, err := api.makeRequest("PUT", uri, lb)
|
||||||
|
if err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r loadBalancerResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return LoadBalancer{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
150
vendor/github.com/cloudflare/cloudflare-go/lockdown.go
generated
vendored
Normal file
150
vendor/github.com/cloudflare/cloudflare-go/lockdown.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneLockdown represents a Zone Lockdown rule. A rule only permits access to
|
||||||
|
// the provided URL pattern(s) from the given IP address(es) or subnet(s).
|
||||||
|
type ZoneLockdown struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
URLs []string `json:"urls"`
|
||||||
|
Configurations []ZoneLockdownConfig `json:"configurations"`
|
||||||
|
Paused bool `json:"paused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneLockdownConfig represents a Zone Lockdown config, which comprises
|
||||||
|
// a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
|
||||||
|
// respectively.)
|
||||||
|
type ZoneLockdownConfig struct {
|
||||||
|
Target string `json:"target"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneLockdownResponse represents a response from the Zone Lockdown endpoint.
|
||||||
|
type ZoneLockdownResponse struct {
|
||||||
|
Result ZoneLockdown `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneLockdownListResponse represents a response from the List Zone Lockdown
|
||||||
|
// endpoint.
|
||||||
|
type ZoneLockdownListResponse struct {
|
||||||
|
Result []ZoneLockdown `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateZoneLockdown creates a Zone ZoneLockdown rule for the given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-create-a-ZoneLockdown-rule
|
||||||
|
func (api *API) CreateZoneLockdown(zoneID string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/lockdowns"
|
||||||
|
res, err := api.makeRequest("POST", uri, ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneLockdownResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateZoneLockdown updates a Zone ZoneLockdown rule (based on the ID) for the
|
||||||
|
// given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-update-ZoneLockdown-rule
|
||||||
|
func (api *API) UpdateZoneLockdown(zoneID string, id string, ld ZoneLockdown) (*ZoneLockdownResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/lockdowns"
|
||||||
|
res, err := api.makeRequest("PUT", uri, ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneLockdownResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteZoneLockdown deletes a Zone ZoneLockdown rule (based on the ID) for the
|
||||||
|
// given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-delete-ZoneLockdown-rule
|
||||||
|
func (api *API) DeleteZoneLockdown(zoneID string, id string) (*ZoneLockdownResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/lockdowns/" + id
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneLockdownResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneLockdown retrieves a Zone ZoneLockdown rule (based on the ID) for the
|
||||||
|
// given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-ZoneLockdown-rule-details
|
||||||
|
func (api *API) ZoneLockdown(zoneID string, id string) (*ZoneLockdownResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/lockdowns/" + id
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneLockdownResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListZoneLockdowns retrieves a list of Zone ZoneLockdown rules for a given
|
||||||
|
// zone ID by page number.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-ZoneLockdown-list-ZoneLockdown-rules
|
||||||
|
func (api *API) ListZoneLockdowns(zoneID string, page int) (*ZoneLockdownListResponse, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
v.Set("per_page", strconv.Itoa(100))
|
||||||
|
query := "?" + v.Encode()
|
||||||
|
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/lockdowns" + query
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneLockdownListResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
90
vendor/github.com/cloudflare/cloudflare-go/options.go
generated
vendored
Normal file
90
vendor/github.com/cloudflare/cloudflare-go/options.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option is a functional option for configuring the API client.
|
||||||
|
type Option func(*API) error
|
||||||
|
|
||||||
|
// HTTPClient accepts a custom *http.Client for making API calls.
|
||||||
|
func HTTPClient(client *http.Client) Option {
|
||||||
|
return func(api *API) error {
|
||||||
|
api.httpClient = client
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headers allows you to set custom HTTP headers when making API calls (e.g. for
|
||||||
|
// satisfying HTTP proxies, or for debugging).
|
||||||
|
func Headers(headers http.Header) Option {
|
||||||
|
return func(api *API) error {
|
||||||
|
api.headers = headers
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Organization allows you to apply account-level changes (Load Balancing, Railguns)
|
||||||
|
// to an organization instead.
|
||||||
|
func UsingOrganization(orgID string) Option {
|
||||||
|
return func(api *API) error {
|
||||||
|
api.organizationID = orgID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsingRateLimit applies a non-default rate limit to client API requests
|
||||||
|
// If not specified the default of 4rps will be applied
|
||||||
|
func UsingRateLimit(rps float64) Option {
|
||||||
|
return func(api *API) error {
|
||||||
|
// because ratelimiter doesnt do any windowing
|
||||||
|
// setting burst makes it difficult to enforce a fixed rate
|
||||||
|
// so setting it equal to 1 this effectively disables bursting
|
||||||
|
// this doesn't check for sensible values, ultimately the api will enforce that the value is ok
|
||||||
|
api.rateLimiter = rate.NewLimiter(rate.Limit(rps), 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsingRetryPolicy applies a non-default number of retries and min/max retry delays
|
||||||
|
// This will be used when the client exponentially backs off after errored requests
|
||||||
|
func UsingRetryPolicy(maxRetries int, minRetryDelaySecs int, maxRetryDelaySecs int) Option {
|
||||||
|
// seconds is very granular for a minimum delay - but this is only in case of failure
|
||||||
|
return func(api *API) error {
|
||||||
|
api.retryPolicy = RetryPolicy{
|
||||||
|
MaxRetries: maxRetries,
|
||||||
|
MinRetryDelay: time.Duration(minRetryDelaySecs) * time.Second,
|
||||||
|
MaxRetryDelay: time.Duration(maxRetryDelaySecs) * time.Second,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UsingLogger can be set if you want to get log output from this API instance
|
||||||
|
// By default no log output is emitted
|
||||||
|
func UsingLogger(logger Logger) Option {
|
||||||
|
return func(api *API) error {
|
||||||
|
api.logger = logger
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOptions parses the supplied options functions and returns a configured
|
||||||
|
// *API instance.
|
||||||
|
func (api *API) parseOptions(opts ...Option) error {
|
||||||
|
// Range over each options function and apply it to our API type to
|
||||||
|
// configure it. Options functions are applied in order, with any
|
||||||
|
// conflicting options overriding earlier calls.
|
||||||
|
for _, option := range opts {
|
||||||
|
err := option(api)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
185
vendor/github.com/cloudflare/cloudflare-go/organizations.go
generated
vendored
Normal file
185
vendor/github.com/cloudflare/cloudflare-go/organizations.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Organization represents a multi-user organization.
|
||||||
|
type Organization struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Permissions []string `json:"permissions,omitempty"`
|
||||||
|
Roles []string `json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizationResponse represents the response from the Organization endpoint.
|
||||||
|
type organizationResponse struct {
|
||||||
|
Response
|
||||||
|
Result []Organization `json:"result"`
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationMember has details on a member.
|
||||||
|
type OrganizationMember struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Roles []OrganizationRole `json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationInvite has details on an invite.
|
||||||
|
type OrganizationInvite struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
InvitedMemberID string `json:"invited_member_id,omitempty"`
|
||||||
|
InvitedMemberEmail string `json:"invited_member_email,omitempty"`
|
||||||
|
OrganizationID string `json:"organization_id,omitempty"`
|
||||||
|
OrganizationName string `json:"organization_name,omitempty"`
|
||||||
|
Roles []OrganizationRole `json:"roles,omitempty"`
|
||||||
|
InvitedBy string `json:"invited_by,omitempty"`
|
||||||
|
InvitedOn *time.Time `json:"invited_on,omitempty"`
|
||||||
|
ExpiresOn *time.Time `json:"expires_on,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationRole has details on a role.
|
||||||
|
type OrganizationRole struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Permissions []string `json:"permissions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationDetails represents details of an organization.
|
||||||
|
type OrganizationDetails struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Members []OrganizationMember `json:"members"`
|
||||||
|
Invites []OrganizationInvite `json:"invites"`
|
||||||
|
Roles []OrganizationRole `json:"roles,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizationDetailsResponse represents the response from the OrganizationDetails endpoint.
|
||||||
|
type organizationDetailsResponse struct {
|
||||||
|
Response
|
||||||
|
Result OrganizationDetails `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOrganizations lists organizations of the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-s-organizations-list-organizations
|
||||||
|
func (api *API) ListOrganizations() ([]Organization, ResultInfo, error) {
|
||||||
|
var r organizationResponse
|
||||||
|
res, err := api.makeRequest("GET", "/user/organizations", nil)
|
||||||
|
if err != nil {
|
||||||
|
return []Organization{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []Organization{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, r.ResultInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationDetails returns details for the specified organization of the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organizations-organization-details
|
||||||
|
func (api *API) OrganizationDetails(organizationID string) (OrganizationDetails, error) {
|
||||||
|
var r organizationDetailsResponse
|
||||||
|
uri := "/organizations/" + organizationID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return OrganizationDetails{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return OrganizationDetails{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizationMembersResponse represents the response from the Organization members endpoint.
|
||||||
|
type organizationMembersResponse struct {
|
||||||
|
Response
|
||||||
|
Result []OrganizationMember `json:"result"`
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationMembers returns list of members for specified organization of the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-members-list-members
|
||||||
|
func (api *API) OrganizationMembers(organizationID string) ([]OrganizationMember, ResultInfo, error) {
|
||||||
|
var r organizationMembersResponse
|
||||||
|
uri := "/organizations/" + organizationID + "/members"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationMember{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationMember{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, r.ResultInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizationInvitesResponse represents the response from the Organization invites endpoint.
|
||||||
|
type organizationInvitesResponse struct {
|
||||||
|
Response
|
||||||
|
Result []OrganizationInvite `json:"result"`
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationMembers returns list of invites for specified organization of the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-invites
|
||||||
|
func (api *API) OrganizationInvites(organizationID string) ([]OrganizationInvite, ResultInfo, error) {
|
||||||
|
var r organizationInvitesResponse
|
||||||
|
uri := "/organizations/" + organizationID + "/invites"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationInvite{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationInvite{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, r.ResultInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizationRolesResponse represents the response from the Organization roles endpoint.
|
||||||
|
type organizationRolesResponse struct {
|
||||||
|
Response
|
||||||
|
Result []OrganizationRole `json:"result"`
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OrganizationRoles returns list of roles for specified organization of the logged-in user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#organization-roles-list-roles
|
||||||
|
func (api *API) OrganizationRoles(organizationID string) ([]OrganizationRole, ResultInfo, error) {
|
||||||
|
var r organizationRolesResponse
|
||||||
|
uri := "/organizations/" + organizationID + "/roles"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationRole{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []OrganizationRole{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, r.ResultInfo, nil
|
||||||
|
}
|
168
vendor/github.com/cloudflare/cloudflare-go/origin_ca.go
generated
vendored
Normal file
168
vendor/github.com/cloudflare/cloudflare-go/origin_ca.go
generated
vendored
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OriginCACertificate represents a Cloudflare-issued certificate.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#cloudflare-ca
|
||||||
|
type OriginCACertificate struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Certificate string `json:"certificate"`
|
||||||
|
Hostnames []string `json:"hostnames"`
|
||||||
|
ExpiresOn time.Time `json:"expires_on"`
|
||||||
|
RequestType string `json:"request_type"`
|
||||||
|
RequestValidity int `json:"requested_validity"`
|
||||||
|
CSR string `json:"csr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginCACertificateListOptions represents the parameters used to list Cloudflare-issued certificates.
|
||||||
|
type OriginCACertificateListOptions struct {
|
||||||
|
ZoneID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginCACertificateID represents the ID of the revoked certificate from the Revoke Certificate endpoint.
|
||||||
|
type OriginCACertificateID struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// originCACertificateResponse represents the response from the Create Certificate and the Certificate Details endpoints.
|
||||||
|
type originCACertificateResponse struct {
|
||||||
|
Response
|
||||||
|
Result OriginCACertificate `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// originCACertificateResponseList represents the response from the List Certificates endpoint.
|
||||||
|
type originCACertificateResponseList struct {
|
||||||
|
Response
|
||||||
|
Result []OriginCACertificate `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// originCACertificateResponseRevoke represents the response from the Revoke Certificate endpoint.
|
||||||
|
type originCACertificateResponseRevoke struct {
|
||||||
|
Response
|
||||||
|
Result OriginCACertificateID `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOriginCertificate creates a Cloudflare-signed certificate.
|
||||||
|
//
|
||||||
|
// This function requires api.APIUserServiceKey be set to your Certificates API key.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#cloudflare-ca-create-certificate
|
||||||
|
func (api *API) CreateOriginCertificate(certificate OriginCACertificate) (*OriginCACertificate, error) {
|
||||||
|
uri := "/certificates"
|
||||||
|
res, err := api.makeRequestWithAuthType("POST", uri, certificate, AuthUserService)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var originResponse *originCACertificateResponse
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &originResponse)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !originResponse.Success {
|
||||||
|
return nil, errors.New(errRequestNotSuccessful)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &originResponse.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginCertificates lists all Cloudflare-issued certificates.
|
||||||
|
//
|
||||||
|
// This function requires api.APIUserServiceKey be set to your Certificates API key.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#cloudflare-ca-list-certificates
|
||||||
|
func (api *API) OriginCertificates(options OriginCACertificateListOptions) ([]OriginCACertificate, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
if options.ZoneID != "" {
|
||||||
|
v.Set("zone_id", options.ZoneID)
|
||||||
|
}
|
||||||
|
uri := "/certificates" + "?" + v.Encode()
|
||||||
|
res, err := api.makeRequestWithAuthType("GET", uri, nil, AuthUserService)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var originResponse *originCACertificateResponseList
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &originResponse)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !originResponse.Success {
|
||||||
|
return nil, errors.New(errRequestNotSuccessful)
|
||||||
|
}
|
||||||
|
|
||||||
|
return originResponse.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OriginCertificate returns the details for a Cloudflare-issued certificate.
|
||||||
|
//
|
||||||
|
// This function requires api.APIUserServiceKey be set to your Certificates API key.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#cloudflare-ca-certificate-details
|
||||||
|
func (api *API) OriginCertificate(certificateID string) (*OriginCACertificate, error) {
|
||||||
|
uri := "/certificates/" + certificateID
|
||||||
|
res, err := api.makeRequestWithAuthType("GET", uri, nil, AuthUserService)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var originResponse *originCACertificateResponse
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &originResponse)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !originResponse.Success {
|
||||||
|
return nil, errors.New(errRequestNotSuccessful)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &originResponse.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeOriginCertificate revokes a created certificate for a zone.
|
||||||
|
//
|
||||||
|
// This function requires api.APIUserServiceKey be set to your Certificates API key.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#cloudflare-ca-revoke-certificate
|
||||||
|
func (api *API) RevokeOriginCertificate(certificateID string) (*OriginCACertificateID, error) {
|
||||||
|
uri := "/certificates/" + certificateID
|
||||||
|
res, err := api.makeRequestWithAuthType("DELETE", uri, nil, AuthUserService)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var originResponse *originCACertificateResponseRevoke
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &originResponse)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !originResponse.Success {
|
||||||
|
return nil, errors.New(errRequestNotSuccessful)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &originResponse.Result, nil
|
||||||
|
|
||||||
|
}
|
206
vendor/github.com/cloudflare/cloudflare-go/page_rules.go
generated
vendored
Normal file
206
vendor/github.com/cloudflare/cloudflare-go/page_rules.go
generated
vendored
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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_railgun
|
||||||
|
disable_security
|
||||||
|
edge_cache_ttl
|
||||||
|
email_obfuscation
|
||||||
|
forwarding_url
|
||||||
|
ip_geolocation
|
||||||
|
mirage
|
||||||
|
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_railgun": "Disable Railgun", // Value of type string
|
||||||
|
"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
|
||||||
|
"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 time.Time `json:"modified_on,omitempty"`
|
||||||
|
CreatedOn time.Time `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
|
||||||
|
func (api *API) CreatePageRule(zoneID string, rule PageRule) (*PageRule, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/pagerules"
|
||||||
|
res, err := api.makeRequest("POST", uri, rule)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r PageRuleDetailResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return &r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPageRules returns all Page Rules for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#page-rules-for-a-zone-list-page-rules
|
||||||
|
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
|
||||||
|
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 you 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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
297
vendor/github.com/cloudflare/cloudflare-go/railgun.go
generated
vendored
Normal file
297
vendor/github.com/cloudflare/cloudflare-go/railgun.go
generated
vendored
Normal file
|
@ -0,0 +1,297 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Railgun represents a Railgun's properties.
|
||||||
|
type Railgun struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
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 time.Time `json:"activated_on"`
|
||||||
|
CreatedOn time.Time `json:"created_on"`
|
||||||
|
ModifiedOn time.Time `json:"modified_on"`
|
||||||
|
UpgradeInfo struct {
|
||||||
|
LatestVersion string `json:"latest_version"`
|
||||||
|
DownloadLink string `json:"download_link"`
|
||||||
|
} `json:"upgrade_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RailgunListOptions represents the parameters used to list railguns.
|
||||||
|
type RailgunListOptions struct {
|
||||||
|
Direction string
|
||||||
|
}
|
||||||
|
|
||||||
|
// railgunResponse represents the response from the Create Railgun and the Railgun Details endpoints.
|
||||||
|
type railgunResponse struct {
|
||||||
|
Response
|
||||||
|
Result Railgun `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// railgunsResponse represents the response from the List Railguns endpoint.
|
||||||
|
type railgunsResponse struct {
|
||||||
|
Response
|
||||||
|
Result []Railgun `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRailgun creates a new Railgun.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-create-railgun
|
||||||
|
func (api *API) CreateRailgun(name string) (Railgun, error) {
|
||||||
|
uri := api.userBaseURL("") + "/railguns"
|
||||||
|
params := struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}{
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
res, err := api.makeRequest("POST", uri, params)
|
||||||
|
if err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r railgunResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRailguns lists Railguns connected to an account.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-list-railguns
|
||||||
|
func (api *API) ListRailguns(options RailgunListOptions) ([]Railgun, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
if options.Direction != "" {
|
||||||
|
v.Set("direction", options.Direction)
|
||||||
|
}
|
||||||
|
uri := api.userBaseURL("") + "/railguns" + "?" + v.Encode()
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r railgunsResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RailgunDetails returns the details for a Railgun.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-railgun-details
|
||||||
|
func (api *API) RailgunDetails(railgunID string) (Railgun, error) {
|
||||||
|
uri := api.userBaseURL("") + "/railguns/" + railgunID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r railgunResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RailgunZones returns the zones that are currently using a Railgun.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-get-zones-connected-to-a-railgun
|
||||||
|
func (api *API) RailgunZones(railgunID string) ([]Zone, error) {
|
||||||
|
uri := api.userBaseURL("") + "/railguns/" + railgunID + "/zones"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r ZonesResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// enableRailgun enables (true) or disables (false) a Railgun for all zones connected to it.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
|
||||||
|
func (api *API) enableRailgun(railgunID string, enable bool) (Railgun, error) {
|
||||||
|
uri := api.userBaseURL("") + "/railguns/" + railgunID
|
||||||
|
params := struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}{
|
||||||
|
Enabled: enable,
|
||||||
|
}
|
||||||
|
res, err := api.makeRequest("PATCH", uri, params)
|
||||||
|
if err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r railgunResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return Railgun{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableRailgun enables a Railgun for all zones connected to it.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
|
||||||
|
func (api *API) EnableRailgun(railgunID string) (Railgun, error) {
|
||||||
|
return api.enableRailgun(railgunID, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableRailgun enables a Railgun for all zones connected to it.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-enable-or-disable-a-railgun
|
||||||
|
func (api *API) DisableRailgun(railgunID string) (Railgun, error) {
|
||||||
|
return api.enableRailgun(railgunID, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRailgun disables and deletes a Railgun.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-delete-railgun
|
||||||
|
func (api *API) DeleteRailgun(railgunID string) error {
|
||||||
|
uri := api.userBaseURL("") + "/railguns/" + railgunID
|
||||||
|
if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRailgun represents the status of a Railgun on a zone.
|
||||||
|
type ZoneRailgun struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Connected bool `json:"connected"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneRailgunResponse represents the response from the Zone Railgun Details endpoint.
|
||||||
|
type zoneRailgunResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneRailgun `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneRailgunsResponse represents the response from the Zone Railgun endpoint.
|
||||||
|
type zoneRailgunsResponse struct {
|
||||||
|
Response
|
||||||
|
Result []ZoneRailgun `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RailgunDiagnosis represents the test results from testing railgun connections
|
||||||
|
// to a zone.
|
||||||
|
type RailgunDiagnosis struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
HostName string `json:"host_name"`
|
||||||
|
HTTPStatus int `json:"http_status"`
|
||||||
|
Railgun string `json:"railgun"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
ResponseStatus string `json:"response_status"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
ElapsedTime string `json:"elapsed_time"`
|
||||||
|
BodySize string `json:"body_size"`
|
||||||
|
BodyHash string `json:"body_hash"`
|
||||||
|
MissingHeaders string `json:"missing_headers"`
|
||||||
|
ConnectionClose bool `json:"connection_close"`
|
||||||
|
Cloudflare string `json:"cloudflare"`
|
||||||
|
CFRay string `json:"cf-ray"`
|
||||||
|
// NOTE: Cloudflare's online API documentation does not yet have definitions
|
||||||
|
// for the following fields. See: https://api.cloudflare.com/#railgun-connections-for-a-zone-test-railgun-connection/
|
||||||
|
CFWANError string `json:"cf-wan-error"`
|
||||||
|
CFCacheStatus string `json:"cf-cache-status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// railgunDiagnosisResponse represents the response from the Test Railgun Connection enpoint.
|
||||||
|
type railgunDiagnosisResponse struct {
|
||||||
|
Response
|
||||||
|
Result RailgunDiagnosis `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRailguns returns the available Railguns for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railguns-for-a-zone-get-available-railguns
|
||||||
|
func (api *API) ZoneRailguns(zoneID string) ([]ZoneRailgun, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/railguns"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneRailgunsResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRailgunDetails returns the configuration for a given Railgun.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railguns-for-a-zone-get-railgun-details
|
||||||
|
func (api *API) ZoneRailgunDetails(zoneID, railgunID string) (ZoneRailgun, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/railguns/" + railgunID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneRailgun{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneRailgunResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return ZoneRailgun{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRailgunConnection tests a Railgun connection for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railgun-connections-for-a-zone-test-railgun-connection
|
||||||
|
func (api *API) TestRailgunConnection(zoneID, railgunID string) (RailgunDiagnosis, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/railguns/" + railgunID + "/diagnose"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return RailgunDiagnosis{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r railgunDiagnosisResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return RailgunDiagnosis{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// connectZoneRailgun connects (true) or disconnects (false) a Railgun for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
|
||||||
|
func (api *API) connectZoneRailgun(zoneID, railgunID string, connect bool) (ZoneRailgun, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/railguns/" + railgunID
|
||||||
|
params := struct {
|
||||||
|
Connected bool `json:"connected"`
|
||||||
|
}{
|
||||||
|
Connected: connect,
|
||||||
|
}
|
||||||
|
res, err := api.makeRequest("PATCH", uri, params)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneRailgun{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneRailgunResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return ZoneRailgun{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectZoneRailgun connects a Railgun for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
|
||||||
|
func (api *API) ConnectZoneRailgun(zoneID, railgunID string) (ZoneRailgun, error) {
|
||||||
|
return api.connectZoneRailgun(zoneID, railgunID, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisconnectZoneRailgun disconnects a Railgun for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#railguns-for-a-zone-connect-or-disconnect-a-railgun
|
||||||
|
func (api *API) DisconnectZoneRailgun(zoneID, railgunID string) (ZoneRailgun, error) {
|
||||||
|
return api.connectZoneRailgun(zoneID, railgunID, false)
|
||||||
|
}
|
195
vendor/github.com/cloudflare/cloudflare-go/rate_limiting.go
generated
vendored
Normal file
195
vendor/github.com/cloudflare/cloudflare-go/rate_limiting.go
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RateLimit is a policy than can be applied to limit traffic within a customer domain
|
||||||
|
type RateLimit struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Disabled bool `json:"disabled,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Match RateLimitTrafficMatcher `json:"match"`
|
||||||
|
Bypass []RateLimitKeyValue `json:"bypass,omitempty"`
|
||||||
|
Threshold int `json:"threshold"`
|
||||||
|
Period int `json:"period"`
|
||||||
|
Action RateLimitAction `json:"action"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitTrafficMatcher contains the rules that will be used to apply a rate limit to traffic
|
||||||
|
type RateLimitTrafficMatcher struct {
|
||||||
|
Request RateLimitRequestMatcher `json:"request"`
|
||||||
|
Response RateLimitResponseMatcher `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitRequestMatcher contains the matching rules pertaining to requests
|
||||||
|
type RateLimitRequestMatcher struct {
|
||||||
|
Methods []string `json:"methods,omitempty"`
|
||||||
|
Schemes []string `json:"schemes,omitempty"`
|
||||||
|
URLPattern string `json:"url,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitResponseMatcher contains the matching rules pertaining to responses
|
||||||
|
type RateLimitResponseMatcher struct {
|
||||||
|
Statuses []int `json:"status,omitempty"`
|
||||||
|
OriginTraffic *bool `json:"origin_traffic,omitempty"` // api defaults to true so we need an explicit empty value
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitKeyValue is k-v formatted as expected in the rate limit description
|
||||||
|
type RateLimitKeyValue struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitAction is the action that will be taken when the rate limit threshold is reached
|
||||||
|
type RateLimitAction struct {
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Response *RateLimitActionResponse `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimitActionResponse is the response that will be returned when rate limit action is triggered
|
||||||
|
type RateLimitActionResponse struct {
|
||||||
|
ContentType string `json:"content_type"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rateLimitResponse struct {
|
||||||
|
Response
|
||||||
|
Result RateLimit `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rateLimitListResponse struct {
|
||||||
|
Response
|
||||||
|
Result []RateLimit `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRateLimit creates a new rate limit for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-create-a-ratelimit
|
||||||
|
func (api *API) CreateRateLimit(zoneID string, limit RateLimit) (RateLimit, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/rate_limits"
|
||||||
|
res, err := api.makeRequest("POST", uri, limit)
|
||||||
|
if err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r rateLimitResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListRateLimits returns Rate Limits for a zone, paginated according to the provided options
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
|
||||||
|
func (api *API) ListRateLimits(zoneID string, pageOpts PaginationOptions) ([]RateLimit, ResultInfo, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
if pageOpts.PerPage > 0 {
|
||||||
|
v.Set("per_page", strconv.Itoa(pageOpts.PerPage))
|
||||||
|
}
|
||||||
|
if pageOpts.Page > 0 {
|
||||||
|
v.Set("page", strconv.Itoa(pageOpts.Page))
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := "/zones/" + zoneID + "/rate_limits"
|
||||||
|
if len(v) > 0 {
|
||||||
|
uri = uri + "?" + v.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r rateLimitListResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []RateLimit{}, ResultInfo{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, r.ResultInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAllRateLimits returns all Rate Limits for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-list-rate-limits
|
||||||
|
func (api *API) ListAllRateLimits(zoneID string) ([]RateLimit, error) {
|
||||||
|
pageOpts := PaginationOptions{
|
||||||
|
PerPage: 100, // this is the max page size allowed
|
||||||
|
Page: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
allRateLimits := make([]RateLimit, 0)
|
||||||
|
for {
|
||||||
|
rateLimits, resultInfo, err := api.ListRateLimits(zoneID, pageOpts)
|
||||||
|
if err != nil {
|
||||||
|
return []RateLimit{}, err
|
||||||
|
}
|
||||||
|
allRateLimits = append(allRateLimits, rateLimits...)
|
||||||
|
// total pages is not returned on this call
|
||||||
|
// if number of records is less than the max, this must be the last page
|
||||||
|
// in case TotalCount % PerPage = 0, the last request will return an empty list
|
||||||
|
if resultInfo.Count < resultInfo.PerPage {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// continue with the next page
|
||||||
|
pageOpts.Page = pageOpts.Page + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRateLimits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RateLimit fetches detail about one Rate Limit for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-rate-limit-details
|
||||||
|
func (api *API) RateLimit(zoneID, limitID string) (RateLimit, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r rateLimitResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRateLimit lets you replace a Rate Limit for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-update-rate-limit
|
||||||
|
func (api *API) UpdateRateLimit(zoneID, limitID string, limit RateLimit) (RateLimit, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||||||
|
res, err := api.makeRequest("PUT", uri, limit)
|
||||||
|
if err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r rateLimitResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return RateLimit{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRateLimit deletes a Rate Limit for a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#rate-limits-for-a-zone-delete-rate-limit
|
||||||
|
func (api *API) DeleteRateLimit(zoneID, limitID string) error {
|
||||||
|
uri := "/zones/" + zoneID + "/rate_limits/" + limitID
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r rateLimitResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
148
vendor/github.com/cloudflare/cloudflare-go/ssl.go
generated
vendored
Normal file
148
vendor/github.com/cloudflare/cloudflare-go/ssl.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ZoneCustomSSL represents custom SSL certificate metadata.
|
||||||
|
type ZoneCustomSSL struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Hosts []string `json:"hosts"`
|
||||||
|
Issuer string `json:"issuer"`
|
||||||
|
Signature string `json:"signature"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
BundleMethod string `json:"bundle_method"`
|
||||||
|
ZoneID string `json:"zone_id"`
|
||||||
|
UploadedOn time.Time `json:"uploaded_on"`
|
||||||
|
ModifiedOn time.Time `json:"modified_on"`
|
||||||
|
ExpiresOn time.Time `json:"expires_on"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
KeylessServer KeylessSSL `json:"keyless_server"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneCustomSSLResponse represents the response from the zone SSL details endpoint.
|
||||||
|
type zoneCustomSSLResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneCustomSSL `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneCustomSSLsResponse represents the response from the zone SSL list endpoint.
|
||||||
|
type zoneCustomSSLsResponse struct {
|
||||||
|
Response
|
||||||
|
Result []ZoneCustomSSL `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneCustomSSLOptions represents the parameters to create or update an existing
|
||||||
|
// custom SSL configuration.
|
||||||
|
type ZoneCustomSSLOptions struct {
|
||||||
|
Certificate string `json:"certificate"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
BundleMethod string `json:"bundle_method,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneCustomSSLPriority represents a certificate's ID and priority. It is a
|
||||||
|
// subset of ZoneCustomSSL used for patch requests.
|
||||||
|
type ZoneCustomSSLPriority struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSSL allows you to add a custom SSL certificate to the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
|
||||||
|
func (api *API) CreateSSL(zoneID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates"
|
||||||
|
res, err := api.makeRequest("POST", uri, options)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneCustomSSLResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListSSL lists the custom certificates for the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
|
||||||
|
func (api *API) ListSSL(zoneID string) ([]ZoneCustomSSL, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneCustomSSLsResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SSLDetails returns the configuration details for a custom SSL certificate.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
|
||||||
|
func (api *API) SSLDetails(zoneID, certificateID string) (ZoneCustomSSL, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneCustomSSLResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateSSL updates (replaces) a custom SSL certificate.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
|
||||||
|
func (api *API) UpdateSSL(zoneID, certificateID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
|
||||||
|
res, err := api.makeRequest("PATCH", uri, options)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneCustomSSLResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReprioritizeSSL allows you to change the priority (which is served for a given
|
||||||
|
// request) of custom SSL certificates associated with the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
|
||||||
|
func (api *API) ReprioritizeSSL(zoneID string, p []ZoneCustomSSLPriority) ([]ZoneCustomSSL, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates/prioritize"
|
||||||
|
params := struct {
|
||||||
|
Certificates []ZoneCustomSSLPriority `json:"certificates"`
|
||||||
|
}{
|
||||||
|
Certificates: p,
|
||||||
|
}
|
||||||
|
res, err := api.makeRequest("PUT", uri, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneCustomSSLsResponse
|
||||||
|
if err := json.Unmarshal(res, &r); err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteSSL deletes a custom SSL certificate from the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
|
||||||
|
func (api *API) DeleteSSL(zoneID, certificateID string) error {
|
||||||
|
uri := "/zones/" + zoneID + "/custom_certificates/" + certificateID
|
||||||
|
if _, err := api.makeRequest("DELETE", uri, nil); err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
113
vendor/github.com/cloudflare/cloudflare-go/user.go
generated
vendored
Normal file
113
vendor/github.com/cloudflare/cloudflare-go/user.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User describes a user account.
|
||||||
|
type User struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Email string `json:"email,omitempty"`
|
||||||
|
FirstName string `json:"first_name,omitempty"`
|
||||||
|
LastName string `json:"last_name,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Telephone string `json:"telephone,omitempty"`
|
||||||
|
Country string `json:"country,omitempty"`
|
||||||
|
Zipcode string `json:"zipcode,omitempty"`
|
||||||
|
CreatedOn *time.Time `json:"created_on,omitempty"`
|
||||||
|
ModifiedOn *time.Time `json:"modified_on,omitempty"`
|
||||||
|
APIKey string `json:"api_key,omitempty"`
|
||||||
|
TwoFA bool `json:"two_factor_authentication_enabled,omitempty"`
|
||||||
|
Betas []string `json:"betas,omitempty"`
|
||||||
|
Organizations []Organization `json:"organizations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserResponse wraps a response containing User accounts.
|
||||||
|
type UserResponse struct {
|
||||||
|
Response
|
||||||
|
Result User `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// userBillingProfileResponse wraps a response containing Billing Profile information.
|
||||||
|
type userBillingProfileResponse struct {
|
||||||
|
Response
|
||||||
|
Result UserBillingProfile
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserBillingProfile contains Billing Profile information.
|
||||||
|
type UserBillingProfile struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
FirstName string `json:"first_name,omitempty"`
|
||||||
|
LastName string `json:"last_name,omitempty"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Address2 string `json:"address2,omitempty"`
|
||||||
|
Company string `json:"company,omitempty"`
|
||||||
|
City string `json:"city,omitempty"`
|
||||||
|
State string `json:"state,omitempty"`
|
||||||
|
ZipCode string `json:"zipcode,omitempty"`
|
||||||
|
Country string `json:"country,omitempty"`
|
||||||
|
Telephone string `json:"telephone,omitempty"`
|
||||||
|
CardNumber string `json:"card_number,omitempty"`
|
||||||
|
CardExpiryYear int `json:"card_expiry_year,omitempty"`
|
||||||
|
CardExpiryMonth int `json:"card_expiry_month,omitempty"`
|
||||||
|
VAT string `json:"vat,omitempty"`
|
||||||
|
CreatedOn *time.Time `json:"created_on,omitempty"`
|
||||||
|
EditedOn *time.Time `json:"edited_on,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserDetails provides 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUser updates the properties of the given user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-update-user
|
||||||
|
func (api *API) UpdateUser(user *User) (User, error) {
|
||||||
|
var r UserResponse
|
||||||
|
res, err := api.makeRequest("PATCH", "/user", user)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserBillingProfile returns the billing profile of the user.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-billing-profile
|
||||||
|
func (api *API) UserBillingProfile() (UserBillingProfile, error) {
|
||||||
|
var r userBillingProfileResponse
|
||||||
|
res, err := api.makeRequest("GET", "/user/billing/profile", nil)
|
||||||
|
if err != nil {
|
||||||
|
return UserBillingProfile{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return UserBillingProfile{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
149
vendor/github.com/cloudflare/cloudflare-go/user_agent.go
generated
vendored
Normal file
149
vendor/github.com/cloudflare/cloudflare-go/user_agent.go
generated
vendored
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserAgentRule represents a User-Agent Block. These rules can be used to
|
||||||
|
// challenge, block or whitelist specific User-Agents for a given zone.
|
||||||
|
type UserAgentRule struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Configuration UserAgentRuleConfig `json:"configuration"`
|
||||||
|
Paused bool `json:"paused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAgentRuleConfig represents a Zone Lockdown config, which comprises
|
||||||
|
// a Target ("ip" or "ip_range") and a Value (an IP address or IP+mask,
|
||||||
|
// respectively.)
|
||||||
|
type UserAgentRuleConfig ZoneLockdownConfig
|
||||||
|
|
||||||
|
// UserAgentRuleResponse represents a response from the Zone Lockdown endpoint.
|
||||||
|
type UserAgentRuleResponse struct {
|
||||||
|
Result UserAgentRule `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAgentRuleListResponse represents a response from the List Zone Lockdown endpoint.
|
||||||
|
type UserAgentRuleListResponse struct {
|
||||||
|
Result []UserAgentRule `json:"result"`
|
||||||
|
Response
|
||||||
|
ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserAgentRule creates a User-Agent Block rule for the given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-agent-blocking-rules-create-a-useragent-rule
|
||||||
|
func (api *API) CreateUserAgentRule(zoneID string, ld UserAgentRule) (*UserAgentRuleResponse, error) {
|
||||||
|
switch ld.Mode {
|
||||||
|
case "block", "challenge", "js_challenge", "whitelist":
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, errors.New(`the User-Agent Block rule mode must be one of "block", "challenge", "js_challenge", "whitelist"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/ua_rules"
|
||||||
|
res, err := api.makeRequest("POST", uri, ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &UserAgentRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateUserAgentRule updates a User-Agent Block rule (based on the ID) for the given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-agent-blocking-rules-update-useragent-rule
|
||||||
|
func (api *API) UpdateUserAgentRule(zoneID string, id string, ld UserAgentRule) (*UserAgentRuleResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id
|
||||||
|
res, err := api.makeRequest("PUT", uri, ld)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &UserAgentRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteUserAgentRule deletes a User-Agent Block rule (based on the ID) for the given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-agent-blocking-rules-delete-useragent-rule
|
||||||
|
func (api *API) DeleteUserAgentRule(zoneID string, id string) (*UserAgentRuleResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &UserAgentRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAgentRule retrieves a User-Agent Block rule (based on the ID) for the given zone ID.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-agent-blocking-rules-useragent-rule-details
|
||||||
|
func (api *API) UserAgentRule(zoneID string, id string) (*UserAgentRuleResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/ua_rules/" + id
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &UserAgentRuleResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListUserAgentRules retrieves a list of User-Agent Block rules for a given zone ID by page number.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#user-agent-blocking-rules-list-useragent-rules
|
||||||
|
func (api *API) ListUserAgentRules(zoneID string, page int) (*UserAgentRuleListResponse, error) {
|
||||||
|
v := url.Values{}
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Set("page", strconv.Itoa(page))
|
||||||
|
v.Set("per_page", strconv.Itoa(100))
|
||||||
|
query := "?" + v.Encode()
|
||||||
|
|
||||||
|
uri := "/zones/" + zoneID + "/firewall/ua_rules" + query
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &UserAgentRuleListResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
125
vendor/github.com/cloudflare/cloudflare-go/virtualdns.go
generated
vendored
Normal file
125
vendor/github.com/cloudflare/cloudflare-go/virtualdns.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VirtualDNS represents a Virtual DNS configuration.
|
||||||
|
type VirtualDNS struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
OriginIPs []string `json:"origin_ips"`
|
||||||
|
VirtualDNSIPs []string `json:"virtual_dns_ips"`
|
||||||
|
MinimumCacheTTL uint `json:"minimum_cache_ttl"`
|
||||||
|
MaximumCacheTTL uint `json:"maximum_cache_ttl"`
|
||||||
|
DeprecateAnyRequests bool `json:"deprecate_any_requests"`
|
||||||
|
ModifiedOn string `json:"modified_on"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualDNSResponse represents a Virtual DNS response.
|
||||||
|
type VirtualDNSResponse struct {
|
||||||
|
Response
|
||||||
|
Result *VirtualDNS `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualDNSListResponse represents an array of Virtual DNS responses.
|
||||||
|
type VirtualDNSListResponse struct {
|
||||||
|
Response
|
||||||
|
Result []*VirtualDNS `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateVirtualDNS creates a new Virtual DNS cluster.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#virtual-dns-users--create-a-virtual-dns-cluster
|
||||||
|
func (api *API) CreateVirtualDNS(v *VirtualDNS) (*VirtualDNS, error) {
|
||||||
|
res, err := api.makeRequest("POST", "/user/virtual_dns", v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &VirtualDNSResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualDNS fetches a single virtual DNS cluster.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#virtual-dns-users--get-a-virtual-dns-cluster
|
||||||
|
func (api *API) VirtualDNS(virtualDNSID string) (*VirtualDNS, error) {
|
||||||
|
uri := "/user/virtual_dns/" + virtualDNSID
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &VirtualDNSResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListVirtualDNS lists the virtual DNS clusters associated with an account.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#virtual-dns-users--get-virtual-dns-clusters
|
||||||
|
func (api *API) ListVirtualDNS() ([]*VirtualDNS, error) {
|
||||||
|
res, err := api.makeRequest("GET", "/user/virtual_dns", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &VirtualDNSListResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVirtualDNS updates a Virtual DNS cluster.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#virtual-dns-users--modify-a-virtual-dns-cluster
|
||||||
|
func (api *API) UpdateVirtualDNS(virtualDNSID string, vv VirtualDNS) error {
|
||||||
|
uri := "/user/virtual_dns/" + virtualDNSID
|
||||||
|
res, err := api.makeRequest("PUT", uri, vv)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &VirtualDNSResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteVirtualDNS deletes a Virtual DNS cluster. Note that this cannot be
|
||||||
|
// undone, and will stop all traffic to that cluster.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#virtual-dns-users--delete-a-virtual-dns-cluster
|
||||||
|
func (api *API) DeleteVirtualDNS(virtualDNSID string) error {
|
||||||
|
uri := "/user/virtual_dns/" + virtualDNSID
|
||||||
|
res, err := api.makeRequest("DELETE", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &VirtualDNSResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
97
vendor/github.com/cloudflare/cloudflare-go/waf.go
generated
vendored
Normal file
97
vendor/github.com/cloudflare/cloudflare-go/waf.go
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WAFPackage represents a WAF package configuration.
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WAFPackagesResponse represents the response from the WAF packages endpoint.
|
||||||
|
type WAFPackagesResponse struct {
|
||||||
|
Response
|
||||||
|
Result []WAFPackage `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WAFRule represents a WAF rule.
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WAFRulesResponse represents the response from the WAF rule endpoint.
|
||||||
|
type WAFRulesResponse struct {
|
||||||
|
Response
|
||||||
|
Result []WAFRule `json:"result"`
|
||||||
|
ResultInfo ResultInfo `json:"result_info"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListWAFPackages returns a slice of the WAF packages for the given zone.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListWAFRules returns a slice of the WAF rules for the given WAF package.
|
||||||
|
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
|
||||||
|
}
|
587
vendor/github.com/cloudflare/cloudflare-go/zone.go
generated
vendored
Normal file
587
vendor/github.com/cloudflare/cloudflare-go/zone.go
generated
vendored
Normal file
|
@ -0,0 +1,587 @@
|
||||||
|
package cloudflare
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Owner describes the resource owner.
|
||||||
|
type Owner struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
OwnerType string `json:"owner_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone describes a Cloudflare zone.
|
||||||
|
type Zone struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
// DevMode contains the time in seconds until development expires (if
|
||||||
|
// positive) or since it expired (if negative). It will be 0 if never used.
|
||||||
|
DevMode int `json:"development_mode"`
|
||||||
|
OriginalNS []string `json:"original_name_servers"`
|
||||||
|
OriginalRegistrar string `json:"original_registrar"`
|
||||||
|
OriginalDNSHost string `json:"original_dnshost"`
|
||||||
|
CreatedOn time.Time `json:"created_on"`
|
||||||
|
ModifiedOn time.Time `json:"modified_on"`
|
||||||
|
NameServers []string `json:"name_servers"`
|
||||||
|
Owner Owner `json:"owner"`
|
||||||
|
Permissions []string `json:"permissions"`
|
||||||
|
Plan ZoneRatePlan `json:"plan"`
|
||||||
|
PlanPending ZoneRatePlan `json:"plan_pending,omitempty"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneMeta describes 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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRatePlan contains the plan information for a zone.
|
||||||
|
type ZoneRatePlan struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Price int `json:"price,omitempty"`
|
||||||
|
Currency string `json:"currency,omitempty"`
|
||||||
|
Duration int `json:"duration,omitempty"`
|
||||||
|
Frequency string `json:"frequency,omitempty"`
|
||||||
|
Components []zoneRatePlanComponents `json:"components,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type zoneRatePlanComponents struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Default int `json:"Default"`
|
||||||
|
UnitPrice int `json:"unit_price"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneID contains only the zone ID.
|
||||||
|
type ZoneID struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneResponse represents the response from the Zone endpoint containing a single zone.
|
||||||
|
type ZoneResponse struct {
|
||||||
|
Response
|
||||||
|
Result Zone `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZonesResponse represents the response from the Zone endpoint containing an array of zones.
|
||||||
|
type ZonesResponse struct {
|
||||||
|
Response
|
||||||
|
Result []Zone `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneIDResponse represents the response from the Zone endpoint, containing only a zone ID.
|
||||||
|
type ZoneIDResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneID `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AvailableZoneRatePlansResponse represents the response from the Available Rate Plans endpoint.
|
||||||
|
type AvailableZoneRatePlansResponse struct {
|
||||||
|
Response
|
||||||
|
Result []ZoneRatePlan `json:"result"`
|
||||||
|
ResultInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneRatePlanResponse represents the response from the Plan Details endpoint.
|
||||||
|
type ZoneRatePlanResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneRatePlan `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSetting contains settings for a zone.
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSettingResponse represents the response from the Zone Setting endpoint.
|
||||||
|
type ZoneSettingResponse struct {
|
||||||
|
Response
|
||||||
|
Result []ZoneSetting `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSSLSetting contains ssl setting for a zone.
|
||||||
|
type ZoneSSLSetting struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Editable bool `json:"editable"`
|
||||||
|
ModifiedOn string `json:"modified_on"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
CertificateStatus string `json:"certificate_status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSettingResponse represents the response from the Zone SSL Setting endpoint.
|
||||||
|
type ZoneSSLSettingResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneSSLSetting `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalyticsData contains totals and timeseries analytics data for a zone.
|
||||||
|
type ZoneAnalyticsData struct {
|
||||||
|
Totals ZoneAnalytics `json:"totals"`
|
||||||
|
Timeseries []ZoneAnalytics `json:"timeseries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneAnalyticsDataResponse represents the response from the Zone Analytics Dashboard endpoint.
|
||||||
|
type zoneAnalyticsDataResponse struct {
|
||||||
|
Response
|
||||||
|
Result ZoneAnalyticsData `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalyticsColocation contains analytics data by datacenter.
|
||||||
|
type ZoneAnalyticsColocation struct {
|
||||||
|
ColocationID string `json:"colo_id"`
|
||||||
|
Timeseries []ZoneAnalytics `json:"timeseries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// zoneAnalyticsColocationResponse represents the response from the Zone Analytics By Co-location endpoint.
|
||||||
|
type zoneAnalyticsColocationResponse struct {
|
||||||
|
Response
|
||||||
|
Result []ZoneAnalyticsColocation `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalytics contains analytics data for a zone.
|
||||||
|
type ZoneAnalytics struct {
|
||||||
|
Since time.Time `json:"since"`
|
||||||
|
Until time.Time `json:"until"`
|
||||||
|
Requests struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
Cached int `json:"cached"`
|
||||||
|
Uncached int `json:"uncached"`
|
||||||
|
ContentType map[string]int `json:"content_type"`
|
||||||
|
Country map[string]int `json:"country"`
|
||||||
|
SSL struct {
|
||||||
|
Encrypted int `json:"encrypted"`
|
||||||
|
Unencrypted int `json:"unencrypted"`
|
||||||
|
} `json:"ssl"`
|
||||||
|
HTTPStatus map[string]int `json:"http_status"`
|
||||||
|
} `json:"requests"`
|
||||||
|
Bandwidth struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
Cached int `json:"cached"`
|
||||||
|
Uncached int `json:"uncached"`
|
||||||
|
ContentType map[string]int `json:"content_type"`
|
||||||
|
Country map[string]int `json:"country"`
|
||||||
|
SSL struct {
|
||||||
|
Encrypted int `json:"encrypted"`
|
||||||
|
Unencrypted int `json:"unencrypted"`
|
||||||
|
} `json:"ssl"`
|
||||||
|
} `json:"bandwidth"`
|
||||||
|
Threats struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
Country map[string]int `json:"country"`
|
||||||
|
Type map[string]int `json:"type"`
|
||||||
|
} `json:"threats"`
|
||||||
|
Pageviews struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
SearchEngines map[string]int `json:"search_engines"`
|
||||||
|
} `json:"pageviews"`
|
||||||
|
Uniques struct {
|
||||||
|
All int `json:"all"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalyticsOptions represents the optional parameters in Zone Analytics
|
||||||
|
// endpoint requests.
|
||||||
|
type ZoneAnalyticsOptions struct {
|
||||||
|
Since *time.Time
|
||||||
|
Until *time.Time
|
||||||
|
Continuous *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PurgeCacheRequest represents the request format made to the purge endpoint.
|
||||||
|
type PurgeCacheRequest struct {
|
||||||
|
Everything bool `json:"purge_everything,omitempty"`
|
||||||
|
// Purge by filepath (exact match). Limit of 30
|
||||||
|
Files []string `json:"files,omitempty"`
|
||||||
|
// Purge by Tag (Enterprise only):
|
||||||
|
// https://support.cloudflare.com/hc/en-us/articles/206596608-How-to-Purge-Cache-Using-Cache-Tags-Enterprise-only-
|
||||||
|
Tags []string `json:"tags,omitempty"`
|
||||||
|
// Purge by hostname - e.g. "assets.example.com"
|
||||||
|
Hosts []string `json:"hosts,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PurgeCacheResponse represents the response from the purge endpoint.
|
||||||
|
type PurgeCacheResponse struct {
|
||||||
|
Response
|
||||||
|
Result struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// newZone describes a new zone.
|
||||||
|
type newZone struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
JumpStart bool `json:"jump_start"`
|
||||||
|
// We use a pointer to get a nil type when the field is empty.
|
||||||
|
// This allows us to completely omit this with json.Marshal().
|
||||||
|
Organization *Organization `json:"organization,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateZone creates a zone on an account.
|
||||||
|
//
|
||||||
|
// Setting jumpstart to true will attempt to automatically scan for existing
|
||||||
|
// DNS records. Setting this to false will create the zone with no DNS records.
|
||||||
|
//
|
||||||
|
// If Organization is non-empty, it must have at least the ID field populated.
|
||||||
|
// This will add the new zone to the specified multi-user organization.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-create-a-zone
|
||||||
|
func (api *API) CreateZone(name string, jumpstart bool, org Organization) (Zone, error) {
|
||||||
|
var newzone newZone
|
||||||
|
newzone.Name = name
|
||||||
|
newzone.JumpStart = jumpstart
|
||||||
|
if org.ID != "" {
|
||||||
|
newzone.Organization = &org
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := api.makeRequest("POST", "/zones", newzone)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r ZoneResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneActivationCheck initiates another zone activation check for newly-created zones.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-initiate-another-zone-activation-check
|
||||||
|
func (api *API) ZoneActivationCheck(zoneID string) (Response, error) {
|
||||||
|
res, err := api.makeRequest("PUT", "/zones/"+zoneID+"/activation_check", nil)
|
||||||
|
if err != nil {
|
||||||
|
return Response{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r Response
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return Response{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListZones lists zones on an account. Optionally takes a list of zone names
|
||||||
|
// to filter against.
|
||||||
|
//
|
||||||
|
// 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 ZonesResponse
|
||||||
|
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 {
|
||||||
|
// TODO: Paginate here. We only grab the first page of results.
|
||||||
|
// Could do this concurrently after the first request by creating a
|
||||||
|
// sync.WaitGroup or just a channel + workers.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneDetails fetches information about a zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-zone-details
|
||||||
|
func (api *API) ZoneDetails(zoneID string) (Zone, error) {
|
||||||
|
res, err := api.makeRequest("GET", "/zones/"+zoneID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r ZoneResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneOptions is a subset of Zone, for editable options.
|
||||||
|
type ZoneOptions struct {
|
||||||
|
Paused *bool `json:"paused,omitempty"`
|
||||||
|
VanityNS []string `json:"vanity_name_servers,omitempty"`
|
||||||
|
Plan *ZoneRatePlan `json:"plan,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSetPaused pauses Cloudflare service for the entire zone, sending all
|
||||||
|
// traffic direct to the origin.
|
||||||
|
func (api *API) ZoneSetPaused(zoneID string, paused bool) (Zone, error) {
|
||||||
|
zoneopts := ZoneOptions{Paused: &paused}
|
||||||
|
zone, err := api.EditZone(zoneID, zoneopts)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSetVanityNS sets custom nameservers for the zone.
|
||||||
|
// These names must be within the same zone.
|
||||||
|
func (api *API) ZoneSetVanityNS(zoneID string, ns []string) (Zone, error) {
|
||||||
|
zoneopts := ZoneOptions{VanityNS: ns}
|
||||||
|
zone, err := api.EditZone(zoneID, zoneopts)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSetRatePlan changes the zone plan.
|
||||||
|
func (api *API) ZoneSetRatePlan(zoneID string, plan ZoneRatePlan) (Zone, error) {
|
||||||
|
zoneopts := ZoneOptions{Plan: &plan}
|
||||||
|
zone, err := api.EditZone(zoneID, zoneopts)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditZone edits the given zone.
|
||||||
|
//
|
||||||
|
// This is usually called by ZoneSetPaused, ZoneSetVanityNS or ZoneSetPlan.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-edit-zone-properties
|
||||||
|
func (api *API) EditZone(zoneID string, zoneOpts ZoneOptions) (Zone, error) {
|
||||||
|
res, err := api.makeRequest("PATCH", "/zones/"+zoneID, zoneOpts)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r ZoneResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return Zone{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PurgeEverything purges the cache for the given zone.
|
||||||
|
//
|
||||||
|
// Note: this will substantially increase load on the origin server for that
|
||||||
|
// zone if there is a high cached vs. uncached request ratio.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-purge-all-files
|
||||||
|
func (api *API) PurgeEverything(zoneID string) (PurgeCacheResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/purge_cache"
|
||||||
|
res, err := api.makeRequest("DELETE", uri, PurgeCacheRequest{true, nil, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// PurgeCache purges the cache using the given PurgeCacheRequest (zone/url/tag).
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-purge-individual-files-by-url-and-cache-tags
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteZone deletes the given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-delete-a-zone
|
||||||
|
func (api *API) DeleteZone(zoneID string) (ZoneID, error) {
|
||||||
|
res, err := api.makeRequest("DELETE", "/zones/"+zoneID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneID{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r ZoneIDResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneID{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AvailableZoneRatePlans returns information about all plans available to the specified zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-plan-available-plans
|
||||||
|
func (api *API) AvailableZoneRatePlans(zoneID string) ([]ZoneRatePlan, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/available_rate_plans"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []ZoneRatePlan{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r AvailableZoneRatePlansResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return []ZoneRatePlan{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode encodes non-nil fields into URL encoded form.
|
||||||
|
func (o ZoneAnalyticsOptions) encode() string {
|
||||||
|
v := url.Values{}
|
||||||
|
if o.Since != nil {
|
||||||
|
v.Set("since", (*o.Since).Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if o.Until != nil {
|
||||||
|
v.Set("until", (*o.Until).Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
if o.Continuous != nil {
|
||||||
|
v.Set("continuous", fmt.Sprintf("%t", *o.Continuous))
|
||||||
|
}
|
||||||
|
return v.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalyticsDashboard returns zone analytics information.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-analytics-dashboard
|
||||||
|
func (api *API) ZoneAnalyticsDashboard(zoneID string, options ZoneAnalyticsOptions) (ZoneAnalyticsData, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/analytics/dashboard" + "?" + options.encode()
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneAnalyticsData{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneAnalyticsDataResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneAnalyticsData{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneAnalyticsByColocation returns zone analytics information by datacenter.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-analytics-analytics-by-co-locations
|
||||||
|
func (api *API) ZoneAnalyticsByColocation(zoneID string, options ZoneAnalyticsOptions) ([]ZoneAnalyticsColocation, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/analytics/colos" + "?" + options.encode()
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r zoneAnalyticsColocationResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSettings returns all of the settings for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-settings-get-all-zone-settings
|
||||||
|
func (api *API) ZoneSettings(zoneID string) (*ZoneSettingResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/settings"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneSettingResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateZoneSettings updates the settings for a given zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-settings-edit-zone-settings-info
|
||||||
|
func (api *API) UpdateZoneSettings(zoneID string, settings []ZoneSetting) (*ZoneSettingResponse, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/settings"
|
||||||
|
res, err := api.makeRequest("PATCH", uri, struct {
|
||||||
|
Items []ZoneSetting `json:"items"`
|
||||||
|
}{settings})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &ZoneSettingResponse{}
|
||||||
|
err = json.Unmarshal(res, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneSSLSettings returns information about SSL setting to the specified zone.
|
||||||
|
//
|
||||||
|
// API reference: https://api.cloudflare.com/#zone-settings-get-ssl-setting
|
||||||
|
func (api *API) ZoneSSLSettings(zoneID string) (ZoneSSLSetting, error) {
|
||||||
|
uri := "/zones/" + zoneID + "/settings/ssl"
|
||||||
|
res, err := api.makeRequest("GET", uri, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneSSLSetting{}, errors.Wrap(err, errMakeRequestError)
|
||||||
|
}
|
||||||
|
var r ZoneSSLSettingResponse
|
||||||
|
err = json.Unmarshal(res, &r)
|
||||||
|
if err != nil {
|
||||||
|
return ZoneSSLSetting{}, errors.Wrap(err, errUnmarshalError)
|
||||||
|
}
|
||||||
|
return r.Result, nil
|
||||||
|
}
|
3
vendor/golang.org/x/time/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/time/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/time/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/time/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
27
vendor/golang.org/x/time/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/time/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 The Go Authors. 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.
|
||||||
|
* Neither the name of Google Inc. 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
|
||||||
|
OWNER 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.
|
22
vendor/golang.org/x/time/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/time/PATENTS
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google 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,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
384
vendor/golang.org/x/time/rate/rate.go
generated
vendored
Normal file
384
vendor/golang.org/x/time/rate/rate.go
generated
vendored
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package rate provides a rate limiter.
|
||||||
|
package rate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Limit defines the maximum frequency of some events.
|
||||||
|
// Limit is represented as number of events per second.
|
||||||
|
// A zero Limit allows no events.
|
||||||
|
type Limit float64
|
||||||
|
|
||||||
|
// Inf is the infinite rate limit; it allows all events (even if burst is zero).
|
||||||
|
const Inf = Limit(math.MaxFloat64)
|
||||||
|
|
||||||
|
// Every converts a minimum time interval between events to a Limit.
|
||||||
|
func Every(interval time.Duration) Limit {
|
||||||
|
if interval <= 0 {
|
||||||
|
return Inf
|
||||||
|
}
|
||||||
|
return 1 / Limit(interval.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Limiter controls how frequently events are allowed to happen.
|
||||||
|
// It implements a "token bucket" of size b, initially full and refilled
|
||||||
|
// at rate r tokens per second.
|
||||||
|
// Informally, in any large enough time interval, the Limiter limits the
|
||||||
|
// rate to r tokens per second, with a maximum burst size of b events.
|
||||||
|
// As a special case, if r == Inf (the infinite rate), b is ignored.
|
||||||
|
// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
|
||||||
|
//
|
||||||
|
// The zero value is a valid Limiter, but it will reject all events.
|
||||||
|
// Use NewLimiter to create non-zero Limiters.
|
||||||
|
//
|
||||||
|
// Limiter has three main methods, Allow, Reserve, and Wait.
|
||||||
|
// Most callers should use Wait.
|
||||||
|
//
|
||||||
|
// Each of the three methods consumes a single token.
|
||||||
|
// They differ in their behavior when no token is available.
|
||||||
|
// If no token is available, Allow returns false.
|
||||||
|
// If no token is available, Reserve returns a reservation for a future token
|
||||||
|
// and the amount of time the caller must wait before using it.
|
||||||
|
// If no token is available, Wait blocks until one can be obtained
|
||||||
|
// or its associated context.Context is canceled.
|
||||||
|
//
|
||||||
|
// The methods AllowN, ReserveN, and WaitN consume n tokens.
|
||||||
|
type Limiter struct {
|
||||||
|
limit Limit
|
||||||
|
burst int
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
tokens float64
|
||||||
|
// last is the last time the limiter's tokens field was updated
|
||||||
|
last time.Time
|
||||||
|
// lastEvent is the latest time of a rate-limited event (past or future)
|
||||||
|
lastEvent time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit returns the maximum overall event rate.
|
||||||
|
func (lim *Limiter) Limit() Limit {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
|
return lim.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burst returns the maximum burst size. Burst is the maximum number of tokens
|
||||||
|
// that can be consumed in a single call to Allow, Reserve, or Wait, so higher
|
||||||
|
// Burst values allow more events to happen at once.
|
||||||
|
// A zero Burst allows no events, unless limit == Inf.
|
||||||
|
func (lim *Limiter) Burst() int {
|
||||||
|
return lim.burst
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLimiter returns a new Limiter that allows events up to rate r and permits
|
||||||
|
// bursts of at most b tokens.
|
||||||
|
func NewLimiter(r Limit, b int) *Limiter {
|
||||||
|
return &Limiter{
|
||||||
|
limit: r,
|
||||||
|
burst: b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow is shorthand for AllowN(time.Now(), 1).
|
||||||
|
func (lim *Limiter) Allow() bool {
|
||||||
|
return lim.AllowN(time.Now(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowN reports whether n events may happen at time now.
|
||||||
|
// Use this method if you intend to drop / skip events that exceed the rate limit.
|
||||||
|
// Otherwise use Reserve or Wait.
|
||||||
|
func (lim *Limiter) AllowN(now time.Time, n int) bool {
|
||||||
|
return lim.reserveN(now, n, 0).ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
|
||||||
|
// A Reservation may be canceled, which may enable the Limiter to permit additional events.
|
||||||
|
type Reservation struct {
|
||||||
|
ok bool
|
||||||
|
lim *Limiter
|
||||||
|
tokens int
|
||||||
|
timeToAct time.Time
|
||||||
|
// This is the Limit at reservation time, it can change later.
|
||||||
|
limit Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK returns whether the limiter can provide the requested number of tokens
|
||||||
|
// within the maximum wait time. If OK is false, Delay returns InfDuration, and
|
||||||
|
// Cancel does nothing.
|
||||||
|
func (r *Reservation) OK() bool {
|
||||||
|
return r.ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay is shorthand for DelayFrom(time.Now()).
|
||||||
|
func (r *Reservation) Delay() time.Duration {
|
||||||
|
return r.DelayFrom(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfDuration is the duration returned by Delay when a Reservation is not OK.
|
||||||
|
const InfDuration = time.Duration(1<<63 - 1)
|
||||||
|
|
||||||
|
// DelayFrom returns the duration for which the reservation holder must wait
|
||||||
|
// before taking the reserved action. Zero duration means act immediately.
|
||||||
|
// InfDuration means the limiter cannot grant the tokens requested in this
|
||||||
|
// Reservation within the maximum wait time.
|
||||||
|
func (r *Reservation) DelayFrom(now time.Time) time.Duration {
|
||||||
|
if !r.ok {
|
||||||
|
return InfDuration
|
||||||
|
}
|
||||||
|
delay := r.timeToAct.Sub(now)
|
||||||
|
if delay < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return delay
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel is shorthand for CancelAt(time.Now()).
|
||||||
|
func (r *Reservation) Cancel() {
|
||||||
|
r.CancelAt(time.Now())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelAt indicates that the reservation holder will not perform the reserved action
|
||||||
|
// and reverses the effects of this Reservation on the rate limit as much as possible,
|
||||||
|
// considering that other reservations may have already been made.
|
||||||
|
func (r *Reservation) CancelAt(now time.Time) {
|
||||||
|
if !r.ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.lim.mu.Lock()
|
||||||
|
defer r.lim.mu.Unlock()
|
||||||
|
|
||||||
|
if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate tokens to restore
|
||||||
|
// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
|
||||||
|
// after r was obtained. These tokens should not be restored.
|
||||||
|
restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct))
|
||||||
|
if restoreTokens <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// advance time to now
|
||||||
|
now, _, tokens := r.lim.advance(now)
|
||||||
|
// calculate new number of tokens
|
||||||
|
tokens += restoreTokens
|
||||||
|
if burst := float64(r.lim.burst); tokens > burst {
|
||||||
|
tokens = burst
|
||||||
|
}
|
||||||
|
// update state
|
||||||
|
r.lim.last = now
|
||||||
|
r.lim.tokens = tokens
|
||||||
|
if r.timeToAct == r.lim.lastEvent {
|
||||||
|
prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens)))
|
||||||
|
if !prevEvent.Before(now) {
|
||||||
|
r.lim.lastEvent = prevEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve is shorthand for ReserveN(time.Now(), 1).
|
||||||
|
func (lim *Limiter) Reserve() *Reservation {
|
||||||
|
return lim.ReserveN(time.Now(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
|
||||||
|
// The Limiter takes this Reservation into account when allowing future events.
|
||||||
|
// ReserveN returns false if n exceeds the Limiter's burst size.
|
||||||
|
// Usage example:
|
||||||
|
// r := lim.ReserveN(time.Now(), 1)
|
||||||
|
// if !r.OK() {
|
||||||
|
// // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// time.Sleep(r.Delay())
|
||||||
|
// Act()
|
||||||
|
// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
|
||||||
|
// If you need to respect a deadline or cancel the delay, use Wait instead.
|
||||||
|
// To drop or skip events exceeding rate limit, use Allow instead.
|
||||||
|
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation {
|
||||||
|
r := lim.reserveN(now, n, InfDuration)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// contextContext is a temporary(?) copy of the context.Context type
|
||||||
|
// to support both Go 1.6 using golang.org/x/net/context and Go 1.7+
|
||||||
|
// with the built-in context package. If people ever stop using Go 1.6
|
||||||
|
// we can remove this.
|
||||||
|
type contextContext interface {
|
||||||
|
Deadline() (deadline time.Time, ok bool)
|
||||||
|
Done() <-chan struct{}
|
||||||
|
Err() error
|
||||||
|
Value(key interface{}) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait is shorthand for WaitN(ctx, 1).
|
||||||
|
func (lim *Limiter) wait(ctx contextContext) (err error) {
|
||||||
|
return lim.WaitN(ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitN blocks until lim permits n events to happen.
|
||||||
|
// It returns an error if n exceeds the Limiter's burst size, the Context is
|
||||||
|
// canceled, or the expected wait time exceeds the Context's Deadline.
|
||||||
|
// The burst limit is ignored if the rate limit is Inf.
|
||||||
|
func (lim *Limiter) waitN(ctx contextContext, n int) (err error) {
|
||||||
|
if n > lim.burst && lim.limit != Inf {
|
||||||
|
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)
|
||||||
|
}
|
||||||
|
// Check if ctx is already cancelled
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// Determine wait limit
|
||||||
|
now := time.Now()
|
||||||
|
waitLimit := InfDuration
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
waitLimit = deadline.Sub(now)
|
||||||
|
}
|
||||||
|
// Reserve
|
||||||
|
r := lim.reserveN(now, n, waitLimit)
|
||||||
|
if !r.ok {
|
||||||
|
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)
|
||||||
|
}
|
||||||
|
// Wait if necessary
|
||||||
|
delay := r.DelayFrom(now)
|
||||||
|
if delay == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := time.NewTimer(delay)
|
||||||
|
defer t.Stop()
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
// We can proceed.
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Context was canceled before we could proceed. Cancel the
|
||||||
|
// reservation, which may permit other events to proceed sooner.
|
||||||
|
r.Cancel()
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
|
||||||
|
func (lim *Limiter) SetLimit(newLimit Limit) {
|
||||||
|
lim.SetLimitAt(time.Now(), newLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
|
||||||
|
// or underutilized by those which reserved (using Reserve or Wait) but did not yet act
|
||||||
|
// before SetLimitAt was called.
|
||||||
|
func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
|
|
||||||
|
now, _, tokens := lim.advance(now)
|
||||||
|
|
||||||
|
lim.last = now
|
||||||
|
lim.tokens = tokens
|
||||||
|
lim.limit = newLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserveN is a helper method for AllowN, ReserveN, and WaitN.
|
||||||
|
// maxFutureReserve specifies the maximum reservation wait duration allowed.
|
||||||
|
// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
|
||||||
|
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
|
||||||
|
lim.mu.Lock()
|
||||||
|
|
||||||
|
if lim.limit == Inf {
|
||||||
|
lim.mu.Unlock()
|
||||||
|
return Reservation{
|
||||||
|
ok: true,
|
||||||
|
lim: lim,
|
||||||
|
tokens: n,
|
||||||
|
timeToAct: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now, last, tokens := lim.advance(now)
|
||||||
|
|
||||||
|
// Calculate the remaining number of tokens resulting from the request.
|
||||||
|
tokens -= float64(n)
|
||||||
|
|
||||||
|
// Calculate the wait duration
|
||||||
|
var waitDuration time.Duration
|
||||||
|
if tokens < 0 {
|
||||||
|
waitDuration = lim.limit.durationFromTokens(-tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide result
|
||||||
|
ok := n <= lim.burst && waitDuration <= maxFutureReserve
|
||||||
|
|
||||||
|
// Prepare reservation
|
||||||
|
r := Reservation{
|
||||||
|
ok: ok,
|
||||||
|
lim: lim,
|
||||||
|
limit: lim.limit,
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
r.tokens = n
|
||||||
|
r.timeToAct = now.Add(waitDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
if ok {
|
||||||
|
lim.last = now
|
||||||
|
lim.tokens = tokens
|
||||||
|
lim.lastEvent = r.timeToAct
|
||||||
|
} else {
|
||||||
|
lim.last = last
|
||||||
|
}
|
||||||
|
|
||||||
|
lim.mu.Unlock()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
||||||
|
// lim is not changed.
|
||||||
|
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
|
||||||
|
last := lim.last
|
||||||
|
if now.Before(last) {
|
||||||
|
last = now
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid making delta overflow below when last is very old.
|
||||||
|
maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)
|
||||||
|
elapsed := now.Sub(last)
|
||||||
|
if elapsed > maxElapsed {
|
||||||
|
elapsed = maxElapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new number of tokens, due to time that passed.
|
||||||
|
delta := lim.limit.tokensFromDuration(elapsed)
|
||||||
|
tokens := lim.tokens + delta
|
||||||
|
if burst := float64(lim.burst); tokens > burst {
|
||||||
|
tokens = burst
|
||||||
|
}
|
||||||
|
|
||||||
|
return now, last, tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// durationFromTokens is a unit conversion function from the number of tokens to the duration
|
||||||
|
// of time it takes to accumulate them at a rate of limit tokens per second.
|
||||||
|
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
|
||||||
|
seconds := tokens / float64(limit)
|
||||||
|
return time.Nanosecond * time.Duration(1e9*seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
|
||||||
|
// which could be accumulated during that duration at a rate of limit tokens per second.
|
||||||
|
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
|
||||||
|
return d.Seconds() * float64(limit)
|
||||||
|
}
|
21
vendor/golang.org/x/time/rate/rate_go16.go
generated
vendored
Normal file
21
vendor/golang.org/x/time/rate/rate_go16.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !go1.7
|
||||||
|
|
||||||
|
package rate
|
||||||
|
|
||||||
|
import "golang.org/x/net/context"
|
||||||
|
|
||||||
|
// Wait is shorthand for WaitN(ctx, 1).
|
||||||
|
func (lim *Limiter) Wait(ctx context.Context) (err error) {
|
||||||
|
return lim.waitN(ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitN blocks until lim permits n events to happen.
|
||||||
|
// It returns an error if n exceeds the Limiter's burst size, the Context is
|
||||||
|
// canceled, or the expected wait time exceeds the Context's Deadline.
|
||||||
|
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
|
||||||
|
return lim.waitN(ctx, n)
|
||||||
|
}
|
21
vendor/golang.org/x/time/rate/rate_go17.go
generated
vendored
Normal file
21
vendor/golang.org/x/time/rate/rate_go17.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package rate
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// Wait is shorthand for WaitN(ctx, 1).
|
||||||
|
func (lim *Limiter) Wait(ctx context.Context) (err error) {
|
||||||
|
return lim.waitN(ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitN blocks until lim permits n events to happen.
|
||||||
|
// It returns an error if n exceeds the Limiter's burst size, the Context is
|
||||||
|
// canceled, or the expected wait time exceeds the Context's Deadline.
|
||||||
|
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
|
||||||
|
return lim.waitN(ctx, n)
|
||||||
|
}
|
Loading…
Reference in a new issue