Skip to content

Commit 0a8d145

Browse files
bltavarescormacrelf
authored andcommitted
Allow managing DIY Controllers
Zerotier provides a central controller on https://my.zerotier.com and it is possible to run our own DIY controller if we desire as well. Currently, this provider uses a hardcoded string pointing to the central controller, which don't allow changes by downstream users. This changes allow overriding the controller url on the provider itself, with default falling back over to the central controller, including documentation changes on `README`. Built on top of #8
1 parent 244987b commit 0a8d145

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ Use `export ZEROTIER_API_KEY="..."`, or define it in a provider block:
8181
```hcl
8282
provider "zerotier" {
8383
api_key = "..."
84+
85+
## Optinal: Override for DIY controller
86+
## Could be overriden by ZEROTIER_CONTROLLER_URL env var or this block
87+
## Defaults to https://my.zerotier.com/api when not provided
88+
# controller_url = "https://my.zerotier.com/api"
8489
}
8590
```
8691

zerotier/client.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import (
1010
"strings"
1111
)
1212

13-
const baseUrl string = "https://my.zerotier.com/api"
14-
1513
type ZeroTierClient struct {
16-
ApiKey string
14+
ApiKey string
15+
Controller string
1716
}
1817

1918
type Route struct {
@@ -200,7 +199,7 @@ func (s *ZeroTierClient) headRequest(req *http.Request) (*http.Response, error)
200199
}
201200

202201
func (client *ZeroTierClient) CheckNetworkExists(id string) (bool, error) {
203-
url := fmt.Sprintf(baseUrl+"/network/%s", id)
202+
url := fmt.Sprintf(client.Controller+"/network/%s", id)
204203
req, err := http.NewRequest("GET", url, nil)
205204
if err != nil {
206205
return false, err
@@ -219,7 +218,7 @@ func (client *ZeroTierClient) CheckNetworkExists(id string) (bool, error) {
219218
}
220219

221220
func (client *ZeroTierClient) GetNetwork(id string) (*Network, error) {
222-
url := fmt.Sprintf(baseUrl+"/network/%s", id)
221+
url := fmt.Sprintf(client.Controller+"/network/%s", id)
223222
req, err := http.NewRequest("GET", url, nil)
224223
if err != nil {
225224
return nil, err
@@ -237,7 +236,7 @@ func (client *ZeroTierClient) GetNetwork(id string) (*Network, error) {
237236
}
238237

239238
func (client *ZeroTierClient) postNetwork(id string, network *Network) (*Network, error) {
240-
url := strings.TrimSuffix(fmt.Sprintf(baseUrl+"/network/%s", id), "/")
239+
url := strings.TrimSuffix(fmt.Sprintf(client.Controller+"/network/%s", id), "/")
241240
// strip carriage returns?
242241
// network.RulesSource = strings.Replace(network.RulesSource, "\r", "", -1)
243242
j, err := json.Marshal(network)
@@ -275,7 +274,7 @@ func (client *ZeroTierClient) UpdateNetwork(id string, network *Network) (*Netwo
275274
}
276275

277276
func (client *ZeroTierClient) DeleteNetwork(id string) error {
278-
url := fmt.Sprintf(baseUrl+"/network/%s", id)
277+
url := fmt.Sprintf(client.Controller+"/network/%s", id)
279278
req, err := http.NewRequest("DELETE", url, nil)
280279
if err != nil {
281280
return err
@@ -289,7 +288,7 @@ func (client *ZeroTierClient) DeleteNetwork(id string) error {
289288
/////////////
290289

291290
func (client *ZeroTierClient) GetMember(nwid string, nodeId string) (*Member, error) {
292-
url := fmt.Sprintf(baseUrl+"/network/%s/member/%s", nwid, nodeId)
291+
url := fmt.Sprintf(client.Controller+"/network/%s/member/%s", nwid, nodeId)
293292
req, err := http.NewRequest("GET", url, nil)
294293
if err != nil {
295294
return nil, err
@@ -307,7 +306,7 @@ func (client *ZeroTierClient) GetMember(nwid string, nodeId string) (*Member, er
307306
}
308307

309308
func (client *ZeroTierClient) postMember(member *Member, reqName string) (*Member, error) {
310-
url := fmt.Sprintf(baseUrl+"/network/%s/member/%s", member.NetworkId, member.NodeId)
309+
url := fmt.Sprintf(client.Controller+"/network/%s/member/%s", member.NetworkId, member.NodeId)
311310
j, err := json.Marshal(member)
312311
if err != nil {
313312
return nil, err
@@ -339,7 +338,7 @@ func (client *ZeroTierClient) UpdateMember(member *Member) (*Member, error) {
339338
// Careful: this one isn't documented in the Zt API,
340339
// but this is what the Central web client does.
341340
func (client *ZeroTierClient) DeleteMember(member *Member) error {
342-
url := fmt.Sprintf(baseUrl+"/network/%s/member/%s", member.NetworkId, member.NodeId)
341+
url := fmt.Sprintf(client.Controller+"/network/%s/member/%s", member.NetworkId, member.NodeId)
343342
req, err := http.NewRequest("DELETE", url, nil)
344343
if err != nil {
345344
return err
@@ -349,7 +348,7 @@ func (client *ZeroTierClient) DeleteMember(member *Member) error {
349348
}
350349

351350
func (client *ZeroTierClient) CheckMemberExists(nwid string, nodeId string) (bool, error) {
352-
url := fmt.Sprintf(baseUrl+"/network/%s/member/%s", nwid, nodeId)
351+
url := fmt.Sprintf(client.Controller+"/network/%s/member/%s", nwid, nodeId)
353352
req, err := http.NewRequest("GET", url, nil)
354353
if err != nil {
355354
return false, err

zerotier/provider.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,40 @@
11
package zerotier
22

33
import (
4+
"fmt"
5+
"net/url"
6+
"strings"
7+
48
"github.com/hashicorp/terraform/helper/schema"
59
"github.com/hashicorp/terraform/terraform"
610
)
711

12+
func isValidControllerURL(i interface{}, k string) ([]string, []error) {
13+
v, ok := i.(string)
14+
if !ok {
15+
return nil, []error{fmt.Errorf("expected type of %q to be string", k)}
16+
}
17+
trimmed := strings.TrimSpace(v)
18+
if trimmed == "" {
19+
return nil, []error{fmt.Errorf("%q must not be empty", k)}
20+
}
21+
22+
if strings.HasSuffix(trimmed, "/") {
23+
return nil, []error{fmt.Errorf("%q should not have trailing slash", k)}
24+
}
25+
26+
parsed, err := url.Parse(trimmed)
27+
if err != nil {
28+
return nil, []error{fmt.Errorf("%q must be a valid url", k)}
29+
}
30+
31+
if parsed.Scheme == "" {
32+
return nil, []error{fmt.Errorf("%q should have an scheme, such as http:// or https://", k)}
33+
}
34+
35+
return nil, nil
36+
}
37+
838
func Provider() terraform.ResourceProvider {
939
return &schema.Provider{
1040
Schema: map[string]*schema.Schema{
@@ -13,6 +43,12 @@ func Provider() terraform.ResourceProvider {
1343
Required: true,
1444
DefaultFunc: schema.EnvDefaultFunc("ZEROTIER_API_KEY", nil),
1545
},
46+
"controller_url": &schema.Schema{
47+
Type: schema.TypeString,
48+
Required: true,
49+
DefaultFunc: schema.EnvDefaultFunc("ZEROTIER_CONTROLLER_URL", "https://my.zerotier.com/api"),
50+
ValidateFunc: isValidControllerURL,
51+
},
1652
},
1753
ResourcesMap: map[string]*schema.Resource{
1854
"zerotier_network": resourceZeroTierNetwork(),
@@ -23,5 +59,7 @@ func Provider() terraform.ResourceProvider {
2359
}
2460

2561
func configureProvider(d *schema.ResourceData) (interface{}, error) {
26-
return &ZeroTierClient{ApiKey: d.Get("api_key").(string)}, nil
62+
return &ZeroTierClient{
63+
ApiKey: d.Get("api_key").(string),
64+
Controller: d.Get("controller_url").(string)}, nil
2765
}

0 commit comments

Comments
 (0)