Skip to content

Commit 488fd69

Browse files
authored
tailscale: support split DNS endpoints (#78)
Support the GET/PATCH/PUT `/api/v2/tailnet/{tailnetID}/dns/split-dns` endpoints for reading, updating, and replacing split DNS settings for a given tailnet respectively. Updates tailscale/corp#19483 Signed-off-by: Mario Minardi <[email protected]>
1 parent 0299382 commit 488fd69

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

tailscale/client.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"time"
1515

1616
"github.com/tailscale/hujson"
17-
1817
"golang.org/x/oauth2/clientcredentials"
1918
)
2019

@@ -333,6 +332,69 @@ func (c *Client) DNSNameservers(ctx context.Context) ([]string, error) {
333332
return resp["dns"], nil
334333
}
335334

335+
// SplitDnsRequest is a map from domain names to a list of nameservers.
336+
type SplitDnsRequest map[string][]string
337+
338+
// SplitDnsResponse is a map from domain names to a list of nameservers.
339+
type SplitDnsResponse SplitDnsRequest
340+
341+
// UpdateSplitDNS updates the split DNS settings for a tailnet using the
342+
// provided SplitDnsRequest object. This is a PATCH operation that performs
343+
// partial updates of the underlying data structure.
344+
//
345+
// Mapping a domain to a nil slice in the request will unset the nameservers
346+
// associated with that domain. Values provided for domains will overwrite the
347+
// current value associated with the domain. Domains not included in the request
348+
// will remain unchanged.
349+
func (c *Client) UpdateSplitDNS(ctx context.Context, request SplitDnsRequest) (SplitDnsResponse, error) {
350+
const uriFmt = "/api/v2/tailnet/%v/dns/split-dns"
351+
352+
req, err := c.buildRequest(ctx, http.MethodPatch, fmt.Sprintf(uriFmt, c.tailnet), requestBody(request))
353+
if err != nil {
354+
return nil, err
355+
}
356+
357+
var resp SplitDnsResponse
358+
if err = c.performRequest(req, &resp); err != nil {
359+
return nil, err
360+
}
361+
362+
return resp, nil
363+
}
364+
365+
// SetSplitDNS sets the split DNS settings for a tailnet using the provided
366+
// SplitDnsRequest object. This is a PUT operation that fully replaces the underlying
367+
// data structure.
368+
//
369+
// Passing in an empty SplitDnsRequest will unset all split DNS mappings for the tailnet.
370+
func (c *Client) SetSplitDNS(ctx context.Context, request SplitDnsRequest) error {
371+
const uriFmt = "/api/v2/tailnet/%v/dns/split-dns"
372+
373+
req, err := c.buildRequest(ctx, http.MethodPut, fmt.Sprintf(uriFmt, c.tailnet), requestBody(request))
374+
if err != nil {
375+
return err
376+
}
377+
378+
return c.performRequest(req, nil)
379+
}
380+
381+
// SplitDNS retrieves the split DNS configuration for a tailnet.
382+
func (c *Client) SplitDNS(ctx context.Context) (SplitDnsResponse, error) {
383+
const uriFmt = "/api/v2/tailnet/%v/dns/split-dns"
384+
385+
req, err := c.buildRequest(ctx, http.MethodGet, fmt.Sprintf(uriFmt, c.tailnet))
386+
if err != nil {
387+
return nil, err
388+
}
389+
390+
var resp SplitDnsResponse
391+
if err = c.performRequest(req, &resp); err != nil {
392+
return nil, err
393+
}
394+
395+
return resp, nil
396+
}
397+
336398
type (
337399
// ACL contains the schema for a tailnet policy file. More details: https://tailscale.com/kb/1018/acls/
338400
ACL struct {

tailscale/client_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,24 @@ func TestClient_DNSSearchPaths(t *testing.T) {
599599
assert.Equal(t, expectedPaths["searchPaths"], paths)
600600
}
601601

602+
func TestClient_SplitDNS(t *testing.T) {
603+
t.Parallel()
604+
605+
client, server := NewTestHarness(t)
606+
server.ResponseCode = http.StatusOK
607+
608+
expectedNameservers := tailscale.SplitDnsResponse{
609+
"example.com": {"1.1.1.1", "1.2.3.4"},
610+
}
611+
612+
server.ResponseBody = expectedNameservers
613+
nameservers, err := client.SplitDNS(context.Background())
614+
assert.NoError(t, err)
615+
assert.Equal(t, http.MethodGet, server.Method)
616+
assert.Equal(t, "/api/v2/tailnet/example.com/dns/split-dns", server.Path)
617+
assert.Equal(t, expectedNameservers, nameservers)
618+
}
619+
602620
func TestClient_SetDNSNameservers(t *testing.T) {
603621
t.Parallel()
604622

@@ -652,6 +670,53 @@ func TestClient_SetDNSSearchPaths(t *testing.T) {
652670
assert.EqualValues(t, paths, body["searchPaths"])
653671
}
654672

673+
func TestClient_UpdateSplitDNS(t *testing.T) {
674+
t.Parallel()
675+
676+
client, server := NewTestHarness(t)
677+
server.ResponseCode = http.StatusOK
678+
679+
nameservers := []string{"1.1.2.1", "3.3.3.4"}
680+
request := tailscale.SplitDnsRequest{
681+
"example.com": nameservers,
682+
}
683+
684+
expectedNameservers := tailscale.SplitDnsResponse{
685+
"example.com": nameservers,
686+
}
687+
server.ResponseBody = expectedNameservers
688+
689+
resp, err := client.UpdateSplitDNS(context.Background(), request)
690+
assert.NoError(t, err)
691+
assert.Equal(t, http.MethodPatch, server.Method)
692+
assert.Equal(t, "/api/v2/tailnet/example.com/dns/split-dns", server.Path)
693+
694+
body := make(tailscale.SplitDnsResponse)
695+
assert.NoError(t, json.Unmarshal(server.Body.Bytes(), &body))
696+
assert.EqualValues(t, nameservers, body["example.com"])
697+
assert.Equal(t, expectedNameservers, resp)
698+
}
699+
700+
func TestClient_SetSplitDNS(t *testing.T) {
701+
t.Parallel()
702+
703+
client, server := NewTestHarness(t)
704+
server.ResponseCode = http.StatusOK
705+
706+
nameservers := []string{"1.1.2.1", "3.3.3.4"}
707+
request := tailscale.SplitDnsRequest{
708+
"example.com": nameservers,
709+
}
710+
711+
assert.NoError(t, client.SetSplitDNS(context.Background(), request))
712+
assert.Equal(t, http.MethodPut, server.Method)
713+
assert.Equal(t, "/api/v2/tailnet/example.com/dns/split-dns", server.Path)
714+
715+
body := make(tailscale.SplitDnsResponse)
716+
assert.NoError(t, json.Unmarshal(server.Body.Bytes(), &body))
717+
assert.EqualValues(t, nameservers, body["example.com"])
718+
}
719+
655720
func TestClient_AuthorizeDevice(t *testing.T) {
656721
t.Parallel()
657722

0 commit comments

Comments
 (0)