Code Monkey home page Code Monkey logo

cloudflare-go's Introduction

cloudflare-go

Go Reference Test Go Report Card

Note: This library is under active development as we expand it to cover our (expanding!) API. Consider the public API of this package a little unstable as we work towards a v1.0.

A Go library for interacting with Cloudflare's API v4. This library allows you to:

  • Manage and automate changes to your DNS records within Cloudflare
  • Manage and automate changes to your zones (domains) on Cloudflare, including adding new zones to your account
  • List and modify the status of WAF (Web Application Firewall) rules for your zones
  • Fetch Cloudflare's IP ranges for automating your firewall whitelisting

A command-line client, flarectl, is also available as part of this project.

Installation

You need a working Go environment. We officially support only currently supported Go versions according to Go project's release policy.

go get github.com/cloudflare/cloudflare-go

Getting Started

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/cloudflare/cloudflare-go"
)

func main() {
	// Construct a new API object using a global API key
	api, err := cloudflare.New(os.Getenv("CLOUDFLARE_API_KEY"), os.Getenv("CLOUDFLARE_API_EMAIL"))
	// alternatively, you can use a scoped API token
	// api, err := cloudflare.NewWithAPIToken(os.Getenv("CLOUDFLARE_API_TOKEN"))
	if err != nil {
		log.Fatal(err)
	}

	// Most API calls require a Context
	ctx := context.Background()

	// Fetch user details on the account
	u, err := api.UserDetails(ctx)
	if err != nil {
		log.Fatal(err)
	}
	// Print user details
	fmt.Println(u)
}

Also refer to the API documentation for how to use this package in-depth.

Experimental improvements

This library is starting to ship with experimental improvements that are not yet ready for production but will be introduced before the next major version. See experimental README for full details.

Contributing

Pull Requests are welcome, but please open an issue (or comment in an existing issue) to discuss any non-trivial changes before submitting code.

License

BSD licensed. See the LICENSE file for details.

cloudflare-go's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cloudflare-go's Issues

ListZones does not paginate

https://github.com/jamesog/cloudflare-go/blob/master/zone.go#L22

    } else {
        res, err = api.makeRequest("GET", "/zones", nil)
        if err != nil {
            return []Zone{}, err
        }
        err = json.Unmarshal(res, &r)
        if err != nil {
            return []Zone{}, err
        }
        zones = r.Result
    }

In the event that a zone (or zones) exist beyond the first page of results, you may not get all the zones for an account.

Ideally we should look at instead calling /zones?per_page=val&page=num, inspecting result_info. in the response and then making additional calls as necessary. Favoring the max. values of per_page and page to reduce total HTTP round trips is probably OK, but we could also take custom values as per #4. The issue is that some endpoints take different min/max values.

Resolve golint Suggestions

I'll probably tackle this, but I'll post it here for posterity:

~/.go/src/github.com/jamesog/cloudflare-go(msilverlock/breaking-options โœ—) golint
cloudflare.go:49:1: comment on exported function NewZone should be of the form "NewZone ..."
cloudflare.go:199:6: exported type ZoneResponse should have comment or be unexported
cloudflare.go:206:6: exported type ZonePlanResponse should have comment or be unexported
cloudflare.go:227:6: exported type ZoneSetting should have comment or be unexported
cloudflare.go:235:6: exported type ZoneSettingResponse should have comment or be unexported
cloudflare.go:242:1: comment on exported type DNSRecord should be of the form "DNSRecord ..." (with optional leading article)
cloudflare.go:261:1: comment on exported type DNSRecordResponse should be of the form "DNSRecordResponse ..." (with optional leading article)
cloudflare.go:269:1: comment on exported type DNSListResponse should be of the form "DNSListResponse ..." (with optional leading article)
cloudflare.go:277:1: comment on exported type ZoneRailgun should be of the form "ZoneRailgun ..." (with optional leading article)
cloudflare.go:285:6: exported type ZoneRailgunResponse should have comment or be unexported
cloudflare.go:292:1: comment on exported type ZoneCustomSSL should be of the form "ZoneCustomSSL ..." (with optional leading article)
cloudflare.go:308:6: exported type ZoneCustomSSLResponse should have comment or be unexported
cloudflare.go:315:6: exported type KeylessSSL should have comment or be unexported
cloudflare.go:327:6: exported type KeylessSSLResponse should have comment or be unexported
cloudflare.go:334:6: exported type Railgun should have comment or be unexported
cloudflare.go:353:6: exported type RailgunResponse should have comment or be unexported
cloudflare.go:360:1: comment on exported type CustomPage should be of the form "CustomPage ..." (with optional leading article)
cloudflare.go:371:6: exported type CustomPageResponse should have comment or be unexported
cloudflare.go:378:1: comment on exported type WAFPackage should be of the form "WAFPackage ..." (with optional leading article)
cloudflare.go:389:6: exported type WAFPackagesResponse should have comment or be unexported
cloudflare.go:400:6: exported type WAFRule should have comment or be unexported
cloudflare.go:414:6: exported type WAFRulesResponse should have comment or be unexported
cloudflare.go:425:6: exported type PurgeCacheRequest should have comment or be unexported
cloudflare.go:431:6: exported type PurgeCacheResponse should have comment or be unexported
dns.go:9:1: comment on exported method API.CreateDNSRecord should be of the form "CreateDNSRecord ..."
dns.go:38:1: comment on exported method API.DNSRecords should be of the form "DNSRecords ..."
dns.go:81:1: comment on exported method API.DNSRecord should be of the form "DNSRecord ..."
dns.go:108:1: comment on exported method API.UpdateDNSRecord should be of the form "UpdateDNSRecord ..."
dns.go:143:1: comment on exported method API.DeleteDNSRecord should be of the form "DeleteDNSRecord ..."
keyless.go:3:1: comment on exported method API.CreateKeyless should be of the form "CreateKeyless ..."
keyless.go:8:1: comment on exported method API.ListKeyless should be of the form "ListKeyless ..."
keyless.go:13:1: comment on exported method API.Keyless should be of the form "Keyless ..."
keyless.go:18:1: comment on exported method API.UpdateKeyless should be of the form "UpdateKeyless ..."
keyless.go:23:1: comment on exported method API.DeleteKeyless should be of the form "DeleteKeyless ..."
railgun.go:5:1: comment on exported method API.CreateRailgun should be of the form "CreateRailgun ..."
railgun.go:24:1: comment on exported method API.Railguns should be of the form "Railguns ..."
railgun.go:29:1: comment on exported method API.Railgun should be of the form "Railgun ..."
railgun.go:34:1: comment on exported method API.ZoneRailgun should be of the form "ZoneRailgun ..."
ssl.go:3:1: comment on exported method API.CreateSSL should be of the form "CreateSSL ..."
ssl.go:8:1: comment on exported method API.ListSSL should be of the form "ListSSL ..."
ssl.go:13:1: comment on exported method API.SSLDetails should be of the form "SSLDetails ..."
ssl.go:18:1: comment on exported method API.UpdateSSL should be of the form "UpdateSSL ..."
ssl.go:23:1: comment on exported method API.ReprioSSL should be of the form "ReprioSSL ..."
ssl.go:28:1: comment on exported method API.DeleteSSL should be of the form "DeleteSSL ..."
user.go:5:1: comment on exported method API.UserDetails should be of the form "UserDetails ..."
user.go:23:1: comment on exported method API.UpdateUser should be of the form "UpdateUser ..."
waf.go:7:1: exported method API.ListWAFPackages should have comment or be unexported
waf.go:23:10: should omit 2nd value from range; this loop is equivalent to `for pi := range ...`
waf.go:29:1: exported method API.ListWAFRules should have comment or be unexported
waf.go:45:10: should omit 2nd value from range; this loop is equivalent to `for ri := range ...`
zone.go:8:1: comment on exported method API.CreateZone should be of the form "CreateZone ..."
zone.go:17:1: comment on exported method API.ListZones should be of the form "ListZones ..."
zone.go:42:12: should omit 2nd value from range; this loop is equivalent to `for zi := range ...`
zone.go:61:1: comment on exported method API.ZoneDetails should be of the form "ZoneDetails ..."
zone.go:92:1: comment on exported function EditZone should be of the form "EditZone ..."
zone.go:97:1: comment on exported method API.PurgeEverything should be of the form "PurgeEverything ..."
zone.go:112:1: comment on exported method API.PurgeCache should be of the form "PurgeCache ..."
zone.go:127:1: comment on exported function DeleteZone should be of the form "DeleteZone ..."

Zone: Use time.Time for time fields

In the Zone type, use time.Time instead of string for the CreatedOn and ModifiedOn fields.

Current:

CreatedOn         string   `json:"created_on"`
ModifiedOn        string   `json:"modified_on"`

Proposed:

CreatedOn         time.Time   `json:"created_on"`
ModifiedOn        time.Time   `json:"modified_on"`

This would make the Zone type similar to ZoneCustomSSL, KeylessSSL, PageRule, etc. that use time.Time for time fields.

Align function signatures

Currently we have a couple of different ways of invoking the functions the library provides. For instance the DNS related functions all take the related zone as a string in the input, the WAF related functions take a zone ID as input and the cache purge and ZoneDetails() methods take a Zone struct as input.

We should probably come to some conclusion of how we want users to interact with the library, and make sure the functions follow that. It will also make it more clear how the function signatures of future functions will look as we get them implemented.

I am suggesting we take a Zone struct as input where possible, it will put the load of looking up the zone on the user, but also means the user is able to cache it however they want, keeping the library leaner and "less magical". Another approach would be to always work on the zone ID, since that is the bare minimum we would need in order to interact with the API. Thoughts?

Potential Breaking Changes

I wanted to discuss these before pushing anything, but I'd like to make some (minor) breaking changes.

These would be caught on compile for those not vendoring the library, and should be easy fixes, but they'll still 'break' things.

  • Change New() to return (*API, error) and enforce non-empty key/email.
  • Add functional options to New so that you can change the underlying http.Client, global pagination options, and other options in the future - e.g. func New(key, email string, option ...Option) where Option is a func (*API) error (see here for examples)
  • Additionally, and less critically, consider returning slices of pointers - e.g. []*DNSRecord - where relevant.

Any changes would also include updating flarectl in the same PR.

LF Feedback: Support Zones, Users and Organizations

Some features are tied to the zone, and others can be added at a single-user or organization level. Having different API functions for each use-case is messy and often not desirable.

We should look at:

  • Allowing the client to be created in a zone-, user- or org- scope - e.g.
// This would create objects on the org (if the object supports it), else against the zone (if it does not)
client, err := cloudflare.New("key", "email", cloudflare.OrgID("ad81388e"))
  • Falling back to the zone as the default when the org isn't specified (TBC)
  • Potentially providing an option to use the user first when there are zone vs. user vs. org options (e.g. IP Firewall, Railgun)

Open to suggestions.

Add contribution guide to project

As noted in #85, it would be good if we had a contribution guide on the repository. GitHub has a bit of documentation about it on the blog, but I'm sure we can find some examples from other projects.

The need that prompted this issue to be created, was that we currently don't have any documentation on the tests being run on all PRs, and suggestions for why they may fail.

Two invalid Page Rules actions supported; two others missing

Currently, pagerules.go:

  • Provides waf and mirage not supported by the API*
  • Is missing automatic_https_rewrites and opportunistic_encryption supported by the API

I'm trying to add page rules to Terraform (hashicorp/terraform/#11249) because I'd like to use them, and found these missing. (Unfortunately for me automatic_https_rewrites was one I personally wanted to use...)

*(Though also, heads-up that if you provide waf as the ID, but give it nonsense as the value, it'll tell you it can only be on or off - as well as the error about it being an invalid setting altogether.)

Can we create a zone from the CLI ?

Per example

docker run -it --rm --env-file $HOME/.cloudflare/env solidnerd/cloudflare \
dns create --zone hillmama.com --name hillmama.com --proxy --type A --content 111.222.333.233

Cant't execute DNSRecord

Hi cloudflare team.

api, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
if err != nil {
	panic(err)
}

res, err := api.DNSRecord("b5b82f8d72414f849bd96a50e6228ece", "645577699")
if err != nil {
	panic(err)
}

I executed this code, error occurred.

panic: error from makeRequest: HTTP status 400: content "{\"success\":false,\"errors\":[{\"code\":7003,\"message\":\"Could not route to \\/zones\\/b5b82f8d72414f849bd96a50e6228ece\\/dns_records\\/645577699, perhaps your object identifier is invalid?\"},{\"code\":7000,\"message\":\"No route for that URI\"}],\"messages\":[],\"result\":null}"

I don't know how to solve this.

Go 1.8
OS : Windows 10 / CentOS 7.3

Healthcheck

Is it possible to an healthcheck on the DNS a domain is pointing to?

Thank you!

[docs] Missing valid PageRuleAction values from documentation

I suppose because this library just passes them straight through without validation, and because the documentation is generated, valid values for PageRuleAction.Value (depending on PageRuleAction.ID are not given.

The majority of them can be easily guessed from the web UI, but not all of them, and some of them would be guessed incorrectly; for example:

  • TTLs can be given as any int (this might be guessed, but from the web UI you might think only the dropdown values are acceptable)
  • SSL > Full Strict is just "strict"
  • Cache levels are way different! 'No Query String' is "basic", 'Ignore Query String' is `"simplified", etc.

It's discoverable, but docs would be helpful. (I just set them no the web UI, then curled to see what it should be.)

I suppose they wouldn't even need to be on cloudflare-go docs if the options were fully enumerated on the main API docs - which, as far as I can tell, they're not.

PurgeCache() not working with App Engine urlfetch HTTP client

Using this api call from the cloudflare-go package:

api, err := cloudflare.New(cloudflareApiKey, cloudflareApiEmail,
    cloudflare.HTTPClient(urlfetch.Client(c)))

resp, err := api.PurgeCache(zoneID, cloudflare.PurgeCacheRequest{
    Files: []string{"http://example.com/foo"},
})

the error response is:

error from makeRequest: HTTP status 400: content "{\"success\":false,\"errors\":[{\"code\":1012,\"message\":\"Request must contain one of \\\"purge_everything\\\" or \\\"files\\\", or \\\"tags\"}],\"messages\":[],\"result\":null}"

Could this be a weird interaction with the App Engine urlfetch HTTP client?

JSON Unmarshal error for CreateDNSRecord

I am trying implementing a dns add service function in my go program so I referred to flarectl and dns.go

record := cf.DNSRecord{
    Name:    r.Name,
    Type:    strings.ToUpper(r.Type),
    Content: r.Content,
    TTL:     int(r.TTL),
    Proxied: r.Proxy,
}

if res, err := d.api.CreateDNSRecord(zoneId, record); err != nil { return nil, err

I get Error unmarshalling JSON: json: Unmarshal(nil *cloudflare.DNSRecordResponse) when running a unit test. I double-checked that I am passing everything correctly by looking at flarectl.go.

DNS record is actually created. Verified by using curl api.

Any help or insights are greatly appreciated!

Very verbose HTTP error messages

When a HTTP error happens on the HTTP error message, the error string that comes back from the message contains the whole HTML error, This means the error messages are long enough to leave a nasty surprise (as I just discovered) :

screen shot 2016-06-13 at 15 19 34

The library should understand 204, No Content responses

DELETE request to the custom hostname endpoint(https://api.cloudflare.com/#custom-hostname-for-a-zone-delete-a-custom-hostname-and-any-issued-ssl-certificates-) returns an empty(incorrect JSON) response with the status of 204. According to the API doc it should return a correct JSON:

{
  "id": "<id of deleted custom hostname>"
}

Assuming the API doc for this specific case is outdated, makeRequest should be able to handle 204 responses instead of returning an error.

flarectl can't run without credentials provided

The error handling made in 586e5a5, means that flarectl no longer can run without the environment variables with credentials being set to at least something. This doesn't give the best user experience, since neither flarectl --help or flarectl ips work any more, even though none of them make API calls which use credentials.

We may have to change flarectl so cloudflare.New() only will be called by the functions that actually need the authenticated API to function. A helper method can be introduced in order to minimise the code duplication. Any other suggestions?

[bug] API client is not thread safe

Current behavior

Crashes when performing multiple api calls using same *cloudflare.API client

Expected behavior

Safely perform multiple api calls using the same client in parallel

Explanation

*cloudflare.API.request is not thread safe and will crash when multiple api requests are made from different goroutines . (This obviously holds true for all of the higher level methods that call it).

The problem happens because we reuse the same http.Header struct across all requests, and http.Header.Set are essentially writes to the underlaying map[string][]string which are not thread safe.

Cannot unmarshal string into int when creating PageRule

I'm seeing a similar issue to #52 but with PageRules, in which they are actually created and I can retrieve fine with curl et al. - but an error is returned:

error unmarshalling the JSON response: json: cannot unmarshal string into Go value of type int

Problems updating DNS record

Hey there, I'm looking into hashicorp/terraform#7690, and trying to determine if the issue is in the terraform code or in the Cloudflare library.

I'm having issues updating a record using cloudflare-go directly (I can create it ok). Wondering if someone can point out if I'm doing something incorrectly, or if this is actually a bug in the library.

I'm pretty new to golang, so not surprised if I'm doing something incorrectly here.

cloudflare.go

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/cloudflare/cloudflare-go"
)

func main() {
	// Construct a new API object
	api, err := cloudflare.New(os.Getenv("CF_API_KEY"), os.Getenv("CF_API_EMAIL"))
	if err != nil {
		log.Fatal(err)
	}

	domain := os.Getenv("CF_DOMAIN")

	// Fetch the zone ID
	zoneId, err := api.ZoneIDByName(domain)
	if err != nil {
		log.Fatal(err)
	}

	newRecord := cloudflare.DNSRecord{
		Type:     "A",
		Name:     "CFAPI",
		Content:  "127.0.0.1",
		Proxied:  false,
		ZoneName: domain,
		ZoneID:   zoneId,
		TTL:      3600,
	}

	log.Printf("[DEBUG] CloudFlare Record create configuration: %#v", newRecord)

	r, err := api.CreateDNSRecord(zoneId, newRecord)
	if err != nil {
		fmt.Errorf("Failed to create record: %s", err)
	}

	recordId := r.Result.ID

	// In the Event that the API returns an empty DNS Record, we verify that the
	// ID returned is not the default ""
	if recordId == "" {
		fmt.Errorf("Failed to find record in Create response; Record was empty")
	}

	log.Printf("[INFO] CloudFlare Record ID: %s", recordId)

	updatedRecord := cloudflare.DNSRecord{
		ID:       recordId,
		Type:     "A",
		Name:     "CFAPI-modified",
		Content:  "192.168.10.10",
		Proxied:  true,
		ZoneName: domain,
		ZoneID:   zoneId,
		TTL:      120,
	}

	log.Printf("[DEBUG] CloudFlare Record update configuration: %#v", updatedRecord)

	err = api.UpdateDNSRecord(zoneId, recordId, updatedRecord)
	if err != nil {
		fmt.Errorf("Failed to update record: %s", err)
	}
}

Output

CF_API_KEY=*** CF_API_EMAIL=*** CF_DOMAIN=wave-juno.com go run cloudflare.go
2016/12/20 15:30:28 [DEBUG] CloudFlare Record create configuration: cloudflare.DNSRecord{ID:"", Type:"A", Name:"CFAPI", Content:"127.0.0.1", Proxiable:false, Proxied:false, TTL:3600, Locked:false, ZoneID:"5058354498babe04ef58c4168949b099", ZoneName:"wave-juno.com", CreatedOn:time.Time{sec:0, nsec:0, loc:(*time.Location)(nil)}, ModifiedOn:time.Time{sec:0, nsec:0, loc:(*time.Location)(nil)}, Data:interface {}(nil), Meta:interface {}(nil), Priority:0}
2016/12/20 15:30:29 [INFO] CloudFlare Record ID: 2dc525087c2ff8b02289eef8a0ec1d68
2016/12/20 15:30:29 [DEBUG] CloudFlare Record update configuration: cloudflare.DNSRecord{ID:"2dc525087c2ff8b02289eef8a0ec1d68", Type:"A", Name:"CFAPI-modified", Content:"192.168.10.10", Proxiable:false, Proxied:true, TTL:120, Locked:false, ZoneID:"5058354498babe04ef58c4168949b099", ZoneName:"wave-juno.com", CreatedOn:time.Time{sec:0, nsec:0, loc:(*time.Location)(nil)}, ModifiedOn:time.Time{sec:0, nsec:0, loc:(*time.Location)(nil)}, Data:interface {}(nil), Meta:interface {}(nil), Priority:0}

Cloudflare Web Console

Expected result

  • Name=cfapi-modified
  • Content=192.168.10.10
  • TTL=120s

SRV records

Hello,

How can I add SRV record with this module?

I'm getting:

2016/08/19 12:38:16.224788 dns.go:93: Error creating DNS record: <nil> error from makeRequest: {"success":false,"errors":[{"code":1004,"message":"DNS Validation Error","error_chain":[{"code":9036,"message":""}]}],"messages":[],"result":null}

Proposal: flarectl Revamp

Summary: Re-factor flarectl to make adding new sub-commands more straightforward.

  • Move to https://github.com/urfave/cli (which is the new, canonical repo for codegangsta/cli) or use https://github.com/bobziuchkovski/writ - which I'm using for my own logshare-cli tool today, with GNU getopt style options.
  • Break sub-commands into their own files
  • Tests so that upstream changes to the library don't break the CLI in subtle ways

Open to suggestions/feedback.

[breaking] Proposal: Make Response types private; expose Results only

Exposing XXXResponse types clutters the public API and offers minimal (if any) benefit to consumers of the package.

Discussion with @jamesog and @saljam internally leads to this proposal:

  • XXXResponse types should be made private
  • We should only expose Response.Result - which will be a *DNSRecord or *PageRule
  • We unwrap the errors from Response.Errors and return them.

In addition we should look to standardize on returning/using pointer types, but that could also be tackled in a separate PR/issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.