Skip to content

Commit

Permalink
Make it work
Browse files Browse the repository at this point in the history
  • Loading branch information
johnmaguire committed Jan 19, 2024
1 parent eaeea83 commit 8ace84d
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 32 deletions.
86 changes: 79 additions & 7 deletions cloudflare.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,92 @@
package main

import "fmt"
import (
"context"
"fmt"

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

type Record struct {
ID string
Name string
}

func IterateRecords(cfToken string, zoneID string, fn func(record Record) error) error {
return fmt.Errorf("not implemented")
func GetZoneID(cf *cloudflare.API, zoneName string) (string, error) {
zones, err := cf.ListZones(context.Background())
if err != nil {
return "", fmt.Errorf("failed to list zones: %w", err)
}

for _, z := range zones {
if z.Name == zoneName {
return z.ID, nil
}
}

return "", fmt.Errorf("zone %s not found", zoneName)
}

func IterateRecords(cf *cloudflare.API, zoneID string, fn func(record Record) error) error {
recs, _, err := cf.ListDNSRecords(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListDNSRecordsParams{})
if err != nil {
return fmt.Errorf("failed to list DNS records: %w", err)
}

for _, r := range recs {
r := Record{ID: r.ID, Name: r.Name}
if err := fn(r); err != nil {
// TODO better error handling
return fmt.Errorf("error in callback for record %+v: %w", r, err)
}
}

return nil
}

func CreateRecord(cfToken string, zoneID string, hostname string, ip string) error {
return fmt.Errorf("not implemented")
func CreateRecord(cf *cloudflare.API, zoneID string, hostname string, ip string) error {
// Check if the record already exists
recs, _, err := cf.ListDNSRecords(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.ListDNSRecordsParams{
Name: hostname,
})
if err != nil {
return fmt.Errorf("failed to list DNS records: %w", err)
}

if len(recs) > 0 {
// update the record
_, err := cf.UpdateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.UpdateDNSRecordParams{
ID: recs[0].ID,
Type: "A",
Name: hostname,
Content: ip,
TTL: 1,
Proxied: cloudflare.BoolPtr(false),
})
if err != nil {
return fmt.Errorf("failed to update DNS record: %w", err)
}
} else {
_, err := cf.CreateDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), cloudflare.CreateDNSRecordParams{
Type: "A",
Name: hostname,
Content: ip,
TTL: 1,
Proxied: cloudflare.BoolPtr(false),
})
if err != nil {
return fmt.Errorf("failed to create DNS record: %w", err)
}
}

return nil
}

func DeleteRecord(cfToken string, zoneID string, recordID string) error {
return fmt.Errorf("not implemented")
func DeleteRecord(cf *cloudflare.API, zoneID string, recordID string) error {
err := cf.DeleteDNSRecord(context.Background(), cloudflare.ZoneIdentifier(zoneID), recordID)
if err != nil {
return fmt.Errorf("failed to delete DNS record: %w", err)
}

return nil
}
13 changes: 11 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,33 @@ type AppConfig struct {
Cloudflare CloudflareConfig `toml:"cloudflare"`

// Defined is the configuration for the Defined Networking API.
DefinedNet DefinedConfig `toml:"defined"`
DefinedNet DefinedConfig `toml:"definednet"`
}

type CloudflareConfig struct {
APIToken string `toml:"api_token"`
ZoneID string `toml:"zone_id"`
ZoneName string `toml:"zone_name"`
}

type DefinedConfig struct {
APIToken string `toml:"api_token"`
}

func LoadConfig(path string) (*AppConfig, error) {
// Load config from file
config, err := newConfigFromFile(path)
if err != nil {
return nil, err
}

// Optionally load secrets from the environment
config.readEnv()

// Default AppendSuffix to the zone name
if config.AppendSuffix == "" {
config.AppendSuffix = config.Cloudflare.ZoneName
}

return config, nil
}

Expand Down
80 changes: 74 additions & 6 deletions defined.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,82 @@
package main

import "fmt"
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)

type Host struct {
ID string
IPAddress string
Hostname string
Tags []string
ID string `json:"id"`
IPAddress string `json:"ipAddress"`
Hostname string `json:"name"`
Tags []string `json:"tags"`
}

type hostsResponse struct {
Data []Host `json:"data"`
Metadata struct {
HasNextPage bool `json:"hasNextPage"`
Cursor string `json:"cursor"`
} `json:"metadata"`
}

func FilterHosts(dnToken string, filterFunc func(Host) bool) ([]Host, error) {
return nil, fmt.Errorf("not implemented")
hosts := []Host{}

cursor := ""
for {
// Fetch the next page of hosts
params := url.Values{
"cursor": []string{cursor},
"pageSize": []string{"500"},
}

req, err := http.NewRequest("GET", "https://api.defined.net/v1/hosts?"+params.Encode(), nil)
if err != nil {
return nil, err
}

req.Header.Set("Accept", "application/json")
req.Header.Set("Authorization", "Bearer "+dnToken)

resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, body)
}

// Decode the response body into a slice of Hosts
var respHosts hostsResponse
err = json.Unmarshal(body, &respHosts)
if err != nil {
return nil, err
}

// Filter the hosts
for _, host := range respHosts.Data {
if filterFunc(host) {
hosts = append(hosts, host)
}
}

// Fetch the next page if there is one
if !respHosts.Metadata.HasNextPage {
break
}
cursor = respHosts.Metadata.Cursor
}

return hosts, nil
}
15 changes: 8 additions & 7 deletions examples/config.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# required_tags is a list of tags that must be present on the host
# for it to be considered for DNS registration
required_tags = ["public-dns:yes"]
# for it to be considered for DNS registration. Optional.
required_tags = ["publish:yes"]
# required_suffix will only register hosts with this domain (must match the
# full domain suffix)
# full domain suffix.) Optional.
required_suffix = ""
# trim_suffix will remove the domain from the hostname (e.g. host.example.com
# will become host)
Expand All @@ -16,11 +16,12 @@ append_suffix = "nebula.example.com"
prune_records = true

[cloudflare]
# api_token is the Cloudflare API token
# api_token is the Cloudflare API token. It needs the `Zone:DNS:Edit`
# permission for the zone specified in zone_name.
api_token = ""
# zone_id is the DNS zone ID to create records in
zone_id = ""
# zone_name is the DNS zone (domain) to create records in (e.g. example.com)
zone_name = ""

[definednet]
# api_token is the Defined.net API token
# api_token is the Defined.net API token. It needs the `hosts:list` permission.
api_token = ""
16 changes: 15 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@ go 1.21.1

require (
github.com/BurntSushi/toml v1.3.2
github.com/cloudflare/cloudflare-go v0.86.0
github.com/urfave/cli/v3 v3.0.0-alpha8
)

require github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
require (
github.com/goccy/go-json v0.10.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/rs/zerolog v1.31.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
)
46 changes: 46 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,60 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/cloudflare/cloudflare-go v0.86.0 h1:jEKN5VHNYNYtfDL2lUFLTRo+nOVNPFxpXTstVx0rqHI=
github.com/cloudflare/cloudflare-go v0.86.0/go.mod h1:wYW/5UP02TUfBToa/yKbQHV+r6h1NnJ1Je7XjuGM4Jw=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/urfave/cli/v3 v3.0.0-alpha8 h1:H+qxFPoCkGzdF8KUMs2fEOZl5io/1QySgUiGfar8occ=
github.com/urfave/cli/v3 v3.0.0-alpha8/go.mod h1:0kK/RUFHyh+yIKSfWxwheGndfnrvYSmYFVeKCh03ZUc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 8ace84d

Please sign in to comment.