forked from cloudflare/cloudflare-go
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement interface to manage custom hostnames (cloudflare#123)
* implement interfcae to manage custom hostnames * place holder UpdateCustomHostnameSSL function * improve documentation
- Loading branch information
1 parent
7a012bf
commit 86147da
Showing
3 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package cloudflare | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestCustomHostname_DeleteCustomHostname(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/zones/foo/custom_hostnames/bar", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, "DELETE", r.Method, "Expected method 'DELETE', got %s", r.Method) | ||
|
||
w.Header().Set("content-type", "application/json") | ||
fmt.Fprintf(w, ` | ||
{ | ||
"id": "bar" | ||
}`) | ||
}) | ||
|
||
err := client.DeleteCustomHostname("foo", "bar") | ||
|
||
assert.NoError(t, err) | ||
} | ||
|
||
func TestCustomHostname_CreateCustomHostname(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/zones/foo/custom_hostnames", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, "POST", r.Method, "Expected method 'POST', got %s", r.Method) | ||
|
||
w.Header().Set("content-type", "application/json") | ||
fmt.Fprintf(w, ` | ||
{ | ||
"success": true, | ||
"errors": [], | ||
"messages": [], | ||
"result": { | ||
"id": "0d89c70d-ad9f-4843-b99f-6cc0252067e9", | ||
"hostname": "app.example.com", | ||
"ssl": { | ||
"status": "pending_validation", | ||
"method": "cname", | ||
"type": "dv", | ||
"cname_target": "dcv.digicert.com", | ||
"cname_name": "810b7d5f01154524b961ba0cd578acc2.app.example.com" | ||
} | ||
} | ||
}`) | ||
}) | ||
|
||
response, err := client.CreateCustomHostname("foo", CustomHostname{Hostname: "app.example.com", SSL: CustomHostnameSSL{Method: "cname", Type: "dv"}}) | ||
|
||
want := &CustomHostnameResponse{ | ||
Result: CustomHostname{ | ||
ID: "0d89c70d-ad9f-4843-b99f-6cc0252067e9", | ||
Hostname: "app.example.com", | ||
SSL: CustomHostnameSSL{ | ||
Type: "dv", | ||
Method: "cname", | ||
Status: "pending_validation", | ||
CnameTarget: "dcv.digicert.com", | ||
CnameName: "810b7d5f01154524b961ba0cd578acc2.app.example.com", | ||
}, | ||
}, | ||
Response: Response{Success: true, Errors: []ResponseInfo{}, Messages: []ResponseInfo{}}, | ||
} | ||
|
||
if assert.NoError(t, err) { | ||
assert.Equal(t, want, response) | ||
} | ||
} | ||
|
||
func TestCustomHostname_CustomHostnames(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/zones/foo/custom_hostnames", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, "GET", r.Method, "Expected method 'GET', got %s", r.Method) | ||
|
||
w.Header().Set("content-type", "application/json") | ||
fmt.Fprintf(w, `{ | ||
"success": true, | ||
"result": [ | ||
{ | ||
"id": "custom_host_1", | ||
"hostname": "custom.host.one", | ||
"ssl": { | ||
"type": "dv", | ||
"method": "cname", | ||
"status": "pending_validation", | ||
"cname_target": "dcv.digicert.com", | ||
"cname_name": "810b7d5f01154524b961ba0cd578acc2.app.example.com" | ||
}, | ||
"custom_metadata": { | ||
"a_random_field": "random field value" | ||
} | ||
} | ||
], | ||
"result_info": { | ||
"page": 1, | ||
"per_page": 20, | ||
"count": 5, | ||
"total_count": 5 | ||
} | ||
}`) | ||
}) | ||
|
||
customHostnames, _, err := client.CustomHostnames("foo", 1, CustomHostname{}) | ||
|
||
want := []CustomHostname{ | ||
{ | ||
ID: "custom_host_1", | ||
Hostname: "custom.host.one", | ||
SSL: CustomHostnameSSL{ | ||
Type: "dv", | ||
Method: "cname", | ||
Status: "pending_validation", | ||
CnameTarget: "dcv.digicert.com", | ||
CnameName: "810b7d5f01154524b961ba0cd578acc2.app.example.com", | ||
}, | ||
CustomMetadata: CustomMetadata{"a_random_field": "random field value"}, | ||
}, | ||
} | ||
|
||
if assert.NoError(t, err) { | ||
assert.Equal(t, want, customHostnames) | ||
} | ||
} | ||
|
||
func TestCustomHostname_CustomHostname(t *testing.T) { | ||
setup() | ||
defer teardown() | ||
|
||
mux.HandleFunc("/zones/foo/custom_hostnames/bar", func(w http.ResponseWriter, r *http.Request) { | ||
assert.Equal(t, "GET", r.Method, "Expected method 'GET', got %s", r.Method) | ||
|
||
w.Header().Set("content-type", "application/json") | ||
fmt.Fprintf(w, `{ | ||
"success": true, | ||
"result": { | ||
"id": "bar", | ||
"hostname": "foo.bar.com", | ||
"ssl": { | ||
"type": "dv", | ||
"method": "http", | ||
"status": "active" | ||
}, | ||
"custom_metadata": { | ||
"origin": "a.custom.origin" | ||
} | ||
} | ||
}`) | ||
}) | ||
|
||
customHostname, err := client.CustomHostname("foo", "bar") | ||
|
||
want := CustomHostname{ | ||
ID: "bar", | ||
Hostname: "foo.bar.com", | ||
SSL: CustomHostnameSSL{ | ||
Status: "active", | ||
Method: "http", | ||
Type: "dv", | ||
}, | ||
CustomMetadata: CustomMetadata{"origin": "a.custom.origin"}, | ||
} | ||
|
||
if assert.NoError(t, err) { | ||
assert.Equal(t, want, customHostname) | ||
} | ||
} |