porkbun-support #1

Merged
forest merged 5 commits from porkbun-support into main 3 months ago
  1. 12
      ReadMe.md
  2. 41
      build-docker.sh
  3. 77
      config.go
  4. 1
      config.json
  5. 131
      gandi_service.go
  6. 126
      main.go
  7. 185
      porkbun_service.go

12
ReadMe.md

@ -21,17 +21,24 @@ It uses a consensus of over 50% of the following 3rd party services in order to
```
{
"PingPongLog": false,
"GandiRecordTTLSeconds": 300,
"HealthPollingSeconds": 20,
"ResetAfterConsecutiveFailures": 3,
"GandiAPIKey": "****************",
"DNSResolvers": [
"8.8.8.8:53",
"9.9.9.9:53",
"1.1.1.1:53"
],
"RegistrarAccounts": [
{
"Label": "sequentialread-gandi",
"Provider": "gandi",
"SecretKey" : "*****************",
"RecordTTLSeconds": 300
}
]
"Domains": [
{
"RegistrarAccountLabel": "sequentialread-gandi",
"Domain": "sequentialread.com",
"Records": [
{
@ -45,6 +52,7 @@ It uses a consensus of over 50% of the following 3rd party services in order to
]
},
{
"RegistrarAccountLabel": "sequentialread-gandi",
"Domain": "server.garden",
"Records": [
{

41
build-docker.sh

@ -1,42 +1,43 @@
#!/bin/bash -e
VERSION="0.0.1"
VERSION="0.1.0"
rm -rf dockerbuild || true
mkdir dockerbuild
#cp Dockerfile dockerbuild/Dockerfile-amd64
cp Dockerfile dockerbuild/Dockerfile-amd64
cp Dockerfile dockerbuild/Dockerfile-arm
#cp Dockerfile dockerbuild/Dockerfile-arm64
#sed -E 's|FROM alpine|FROM amd64/alpine|' -i dockerbuild/Dockerfile-amd64
sed -E 's|FROM alpine|FROM amd64/alpine|' -i dockerbuild/Dockerfile-amd64
sed -E 's|FROM alpine|FROM arm32v7/alpine|' -i dockerbuild/Dockerfile-arm
#sed -E 's|FROM alpine|FROM arm64v8/alpine|' -i dockerbuild/Dockerfile-arm64
#sed -E 's/GOARCH=/GOARCH=amd64/' -i dockerbuild/Dockerfile-amd64
sed -E 's/GOARCH=/GOARCH=amd64/' -i dockerbuild/Dockerfile-amd64
sed -E 's/GOARCH=/GOARCH=arm/' -i dockerbuild/Dockerfile-arm
#sed -E 's/GOARCH=/GOARCH=arm64/' -i dockerbuild/Dockerfile-arm64
#docker build -f dockerbuild/Dockerfile-amd64 -t sequentialread/gandi-dns-updater:$VERSION-amd64 .
docker build -f dockerbuild/Dockerfile-arm -t sequentialread/gandi-dns-updater:$VERSION-arm .
#docker build -f dockerbuild/Dockerfile-arm64 -t sequentialread/gandi-dns-updater:$VERSION-arm64 .
docker build -f dockerbuild/Dockerfile-amd64 -t sequentialread/dns-updater:$VERSION-amd64 .
docker build -f dockerbuild/Dockerfile-arm -t sequentialread/dns-updater:$VERSION-arm .
#docker build -f dockerbuild/Dockerfile-arm64 -t sequentialread/dns-updater:$VERSION-arm64 .
#docker push sequentialread/gandi-dns-updater:$VERSION-amd64
docker push sequentialread/gandi-dns-updater:$VERSION-arm
#docker push sequentialread/gandi-dns-updater:$VERSION-arm64
docker push sequentialread/dns-updater:$VERSION-amd64
docker push sequentialread/dns-updater:$VERSION-arm
#docker push sequentialread/dns-updater:$VERSION-arm64
export DOCKER_CLI_EXPERIMENTAL=enabled
# docker manifest create sequentialread/gandi-dns-updater:$VERSION \
# sequentialread/gandi-dns-updater:$VERSION-amd64 \
# sequentialread/gandi-dns-updater:$VERSION-arm \
# sequentialread/gandi-dns-updater:$VERSION-arm64
# docker manifest create sequentialread/dns-updater:$VERSION \
# sequentialread/dns-updater:$VERSION-amd64 \
# sequentialread/dns-updater:$VERSION-arm \
# sequentialread/dns-updater:$VERSION-arm64
docker manifest create sequentialread/gandi-dns-updater:$VERSION \
sequentialread/gandi-dns-updater:$VERSION-arm \
docker manifest create sequentialread/dns-updater:$VERSION \
sequentialread/dns-updater:$VERSION-arm \
sequentialread/dns-updater:$VERSION-amd64
#docker manifest annotate --arch amd64 sequentialread/gandi-dns-updater:$VERSION sequentialread/gandi-dns-updater:$VERSION-amd64
docker manifest annotate --arch arm sequentialread/gandi-dns-updater:$VERSION sequentialread/gandi-dns-updater:$VERSION-arm
#docker manifest annotate --arch arm64 sequentialread/gandi-dns-updater:$VERSION sequentialread/gandi-dns-updater:$VERSION-arm64
docker manifest annotate --arch amd64 sequentialread/dns-updater:$VERSION sequentialread/dns-updater:$VERSION-amd64
docker manifest annotate --arch arm sequentialread/dns-updater:$VERSION sequentialread/dns-updater:$VERSION-arm
#docker manifest annotate --arch arm64 sequentialread/dns-updater:$VERSION sequentialread/dns-updater:$VERSION-arm64
docker manifest push sequentialread/gandi-dns-updater:$VERSION
docker manifest push sequentialread/dns-updater:$VERSION

77
config.go

@ -5,11 +5,47 @@ import (
"log"
"reflect"
"regexp"
"strings"
configlite "git.sequentialread.com/forest/config-lite"
errorspkg "git.sequentialread.com/forest/pkg-errors"
)
type Config struct {
PingPongLog bool
ResetAfterConsecutiveFailures int
HealthPollingSeconds int
RegistrarAccounts []RegistrarAccount
DNSResolvers []string
Domains []DomainConfig
}
type RegistrarAccount struct {
Label string
Provider string
KeyId string
SecretKey string
RecordTTLSeconds int
}
type DomainConfig struct {
RegistrarAccountLabel string
Domain string
Records []RecordConfig
}
type RecordConfig struct {
Name string
Type string
}
func (record RecordConfig) FQDN(domain string) string {
if record.Name == "@" {
return domain
}
return strings.Join([]string{record.Name, domain}, ".")
}
func GetConfig() *Config {
config := Config{}
@ -18,26 +54,49 @@ func GetConfig() *Config {
panic(errorspkg.Wrap(err, "ReadConfiguration returned"))
}
if config.GandiAPIKey == "" {
panic(errorspkg.New("GandiAPIKey is required"))
if len(config.RegistrarAccounts) < 1 {
log.Fatal("at least one RegistrarAccount is required")
}
if len(config.Domains) == 0 {
panic(errorspkg.New("at least one domain is required"))
}
if config.GandiRecordTTLSeconds < 300 {
panic(errorspkg.New("GandiRecordTTLSeconds must be 300 or greater"))
log.Fatal("at least one domain is required")
}
if config.ResetAfterConsecutiveFailures < 3 {
panic(errorspkg.New("ResetAfterConsecutiveFailures must be 3 or greater"))
log.Fatal("ResetAfterConsecutiveFailures must be 3 or greater")
}
if config.HealthPollingSeconds < 5 {
panic(errorspkg.New("HealthPollingSeconds must be 5 or greater"))
log.Fatal("HealthPollingSeconds must be 5 or greater")
}
accountLabels := map[string]bool{}
for _, info := range config.RegistrarAccounts {
if info.Provider != "gandi" && info.Provider != "porkbun" {
log.Fatalf("Provider '%s' is not supported. supported Providers: gandi, porkbun", info.Provider)
}
if accountLabels[info.Label] {
log.Fatalf("Two accounts have the same label '%s' ", info.Label)
}
accountLabels[info.Label] = true
if info.RecordTTLSeconds < 300 {
log.Fatalf("RecordTTLSeconds on %s account '%s' must be 300 or greater", info.Provider, info.Label)
}
}
for _, domain := range config.Domains {
if !accountLabels[domain.RegistrarAccountLabel] {
log.Fatalf(
"Domain '%s' references RegistrarAccountLabel '%s' which is not in RegistrarAccounts",
domain.Domain, domain.RegistrarAccountLabel,
)
}
}
log.Println("👳 Gandi DNS Updater starting up ✌ with config:")
configToLogBytes, _ := json.MarshalIndent(config, "", " ")
configToLogString := regexp.MustCompile(
`("GandiAPIKey": ")[^"]+(",)`,
`("APIKey": ")[^"]+(",)`,
).ReplaceAllString(
string(configToLogBytes),
"$1******$2",

1
config.json

@ -1,5 +1,4 @@
{
"GandiRecordTTLSeconds": 300,
"HealthPollingSeconds": 10,
"ResetAfterConsecutiveFailures": 10
}

131
gandi_service.go

@ -1,9 +1,12 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"time"
@ -11,9 +14,10 @@ import (
)
type GandiService struct {
Client *http.Client
APIUrl string
APIKey string
Client *http.Client
APIUrl string
APIKey string
RecordTTLSeconds int
}
type GandiDomainRecords struct {
@ -28,55 +32,94 @@ type GandiDomainRecord struct {
TTL int `json:"rrset_ttl"`
}
func NewGandiService(apiKey string) *GandiService {
func NewGandiService(apiKey string, recordTTLSeconds int) *GandiService {
return &GandiService{
Client: &http.Client{Timeout: 30 * time.Second},
APIUrl: "https://api.gandi.net/v5",
APIKey: apiKey,
Client: &http.Client{Timeout: 30 * time.Second},
APIUrl: "https://api.gandi.net/v5",
APIKey: apiKey,
RecordTTLSeconds: recordTTLSeconds,
}
}
// func (service *GandiService) UpdateFreeSubdomains(freeSubdomains map[string][]string) error {
// requestBody := GandiDomainRecords{
// Items: []GandiDomainRecord{},
// }
// for subdomain, ips := range freeSubdomains {
// requestBody.Items = append(
// requestBody.Items,
// GandiDomainRecord{
// Name: subdomain,
// Type: "A",
// Values: ips,
// TTL: 300,
// },
// GandiDomainRecord{
// Name: fmt.Sprintf("*.%s", subdomain),
// Type: "A",
// Values: ips,
// TTL: 300,
// },
// )
// }
func (service *GandiService) TryToUpdateRecordsForDomain(domain DomainConfig, currentPublicIPv4 string) bool {
path := fmt.Sprintf("/livedns/domains/%s/records", domain.Domain)
statusCode, responseBytes, err := service.GandiHTTP("GET", path, nil)
if err != nil {
// if gandi isn't responding at all, we can just give up and try again in 10 seconds.
log.Println("waiting for gandi to respond...")
return false
}
if statusCode >= 300 {
// if we got a non-200 status code from gandi, thats a fatal error. Log it.
log.Printf("gandi (GET %s) returned http %d: \n%s\n\n", path, statusCode, responseBytes)
return false
}
var gandiResourceRecords []GandiDomainRecord
err = json.Unmarshal(responseBytes, &gandiResourceRecords)
if err != nil {
log.Printf("gandi (GET %s) returned http %d, but it didn't return valid json: %s: \n%s\n\n", path, statusCode, err, responseBytes)
return false
}
// if len(requestBody.Items) == 0 {
// return nil
// }
configIndexesToAdd := map[int]bool{}
for i, _ := range domain.Records {
configIndexesToAdd[i] = true
}
newRecords := []GandiDomainRecord{}
for _, gandiRecord := range gandiResourceRecords {
addedConfigRecord := false
for i, configRecord := range domain.Records {
// log.Println(configRecord.Name == gandiRecord.Name, " AND ", configRecord.Type == gandiRecord.Type,
// " | ", configRecord.Name, "==", gandiRecord.Name, " AND ", configRecord.Type, "==", gandiRecord.Type)
if configRecord.Name == gandiRecord.Name && recordTypesMatch(configRecord.Type, gandiRecord.Type) {
if configIndexesToAdd[i] && (len(gandiRecord.Values) == 0 || gandiRecord.Values[0] != currentPublicIPv4) {
configIndexesToAdd[i] = false
addedConfigRecord = true
newRecords = append(newRecords, GandiDomainRecord{
Name: configRecord.Name,
Type: configRecord.Type,
TTL: service.RecordTTLSeconds,
Values: []string{currentPublicIPv4},
})
}
}
}
if !addedConfigRecord {
newRecords = append(newRecords, gandiRecord)
}
}
for i, configRecord := range domain.Records {
if configIndexesToAdd[i] {
newRecords = append([]GandiDomainRecord{{
Name: configRecord.Name,
Type: configRecord.Type,
TTL: service.RecordTTLSeconds,
Values: []string{currentPublicIPv4},
}}, newRecords...)
}
}
// requestBodyBytes, err := json.Marshal(requestBody)
// if err != nil {
// return err
// }
requestBody := GandiDomainRecords{
RemoveApexNS: false,
Items: newRecords,
}
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
panic(err)
}
// endpoint := fmt.Sprintf("/livedns/domains/%s/records", "")
// statusCode, responseBytes, err := service.GandiHTTP("PUT", endpoint, bytes.NewBuffer(requestBodyBytes))
// if err != nil {
// log.Printf("Failed to update free subdomains on gandi: %s \n\nResponse was: %s\n\n", err, string(responseBytes))
// return err
// }
log.Printf("replacing all resource records for the domain %s...\n%s\n\n", domain.Domain, string(requestBodyBytes))
endpoint := fmt.Sprintf("/livedns/domains/%s/records", domain.Domain)
statusCode, responseBytes, err = service.GandiHTTP("PUT", endpoint, bytes.NewBuffer(requestBodyBytes))
if err != nil {
panic(err)
}
if statusCode >= 300 {
log.Printf("gandi (PUT %s) returned http %d. we PUT the data: %s\n\n and gandi responded with:\n%s\n\n", endpoint, statusCode, string(requestBodyBytes), responseBytes)
}
// return nil
// }
return true
}
func (service *GandiService) GandiHTTP(method string, path string, body io.Reader) (int, []byte, error) {
return service.MyHTTP(

126
main.go

@ -1,7 +1,6 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
@ -16,26 +15,6 @@ import (
dnsresolver "github.com/bogdanovich/dns_resolver"
)
type Config struct {
PingPongLog bool
ResetAfterConsecutiveFailures int
HealthPollingSeconds int
GandiRecordTTLSeconds int
GandiAPIKey string
DNSResolvers []string
Domains []DomainConfig
}
type DomainConfig struct {
Domain string
Records []RecordConfig
}
type RecordConfig struct {
Name string
Type string
}
type getPublicIPFunc func() (string, error)
type taskResult struct {
@ -49,6 +28,10 @@ type domainState struct {
failures int
}
type registrarAccount interface {
TryToUpdateRecordsForDomain(domain DomainConfig, currentPublicIPv4 string) bool
}
var lastConsensusIpv4GeneratedAt time.Time
var lastConsensusIpv4 string
var lastConsensusIpv4Err error
@ -64,6 +47,8 @@ var dnsRecordTypesWithSameSemantics = [][]string{
var dnsResolvers map[string]*dnsresolver.DnsResolver
var registrarAccounts map[string]registrarAccount
func main() {
var err error
@ -87,7 +72,16 @@ func main() {
dnsResolvers[address] = dnsresolver.New(ipAddressStrings)
}
gandi := NewGandiService(config.GandiAPIKey)
registrarAccounts = make(map[string]registrarAccount)
for _, accountInfo := range config.RegistrarAccounts {
if accountInfo.Provider == "gandi" {
registrarAccounts[accountInfo.Label] = NewGandiService(accountInfo.SecretKey, accountInfo.RecordTTLSeconds)
}
if accountInfo.Provider == "porkbun" {
registrarAccounts[accountInfo.Label] = NewPorkbunService(accountInfo.KeyId, accountInfo.SecretKey, accountInfo.RecordTTLSeconds)
}
}
// this sets lastConsensusIpv4
waitSeconds := 5
@ -111,7 +105,7 @@ func main() {
monitorChannel := make(chan bool)
go constantlyMonitorDomains(config, gandi, monitorChannel)
go constantlyMonitorDomains(config, monitorChannel)
lastMonitorUpdate := time.Now()
for {
@ -135,7 +129,7 @@ func main() {
}
func constantlyMonitorDomains(config *Config, gandi *GandiService, monitorChannel chan bool) {
func constantlyMonitorDomains(config *Config, monitorChannel chan bool) {
debug.SetPanicOnFault(true)
@ -265,7 +259,8 @@ func constantlyMonitorDomains(config *Config, gandi *GandiService, monitorChanne
currentPublicIPv4, err := getConsensusPublicIpv4WithCache(config)
if err == nil && state.recentlySetIPv4 != currentPublicIPv4 {
updated := tryToUpdateGandiRecordsForDomain(gandi, config, domain, currentPublicIPv4)
registrarAccount := registrarAccounts[domain.RegistrarAccountLabel]
updated := registrarAccount.TryToUpdateRecordsForDomain(domain, currentPublicIPv4)
if updated {
state.recentlySetIPv4 = currentPublicIPv4
}
@ -282,87 +277,6 @@ func constantlyMonitorDomains(config *Config, gandi *GandiService, monitorChanne
time.Sleep(time.Second * time.Duration(config.HealthPollingSeconds))
}
}
func tryToUpdateGandiRecordsForDomain(gandi *GandiService, config *Config, domain DomainConfig, currentPublicIPv4 string) bool {
path := fmt.Sprintf("/livedns/domains/%s/records", domain.Domain)
statusCode, responseBytes, err := gandi.GandiHTTP("GET", path, nil)
if err != nil {
// if gandi isn't responding at all, we can just give up and try again in 10 seconds.
log.Println("waiting for gandi to respond...")
return false
}
if statusCode >= 300 {
// if we got a non-200 status code from gandi, thats a fatal error. Log it.
log.Printf("gandi (GET %s) returned http %d: \n%s\n\n", path, statusCode, responseBytes)
return false
}
var gandiResourceRecords []GandiDomainRecord
err = json.Unmarshal(responseBytes, &gandiResourceRecords)
if err != nil {
log.Printf("gandi (GET %s) returned http %d, but it didn't return valid json: %s: \n%s\n\n", path, statusCode, err, responseBytes)
return false
}
configIndexesToAdd := map[int]bool{}
for i, _ := range domain.Records {
configIndexesToAdd[i] = true
}
newRecords := []GandiDomainRecord{}
for _, gandiRecord := range gandiResourceRecords {
addedConfigRecord := false
for i, configRecord := range domain.Records {
// log.Println(configRecord.Name == gandiRecord.Name, " AND ", configRecord.Type == gandiRecord.Type,
// " | ", configRecord.Name, "==", gandiRecord.Name, " AND ", configRecord.Type, "==", gandiRecord.Type)
if configRecord.Name == gandiRecord.Name && recordTypesMatch(configRecord.Type, gandiRecord.Type) {
if configIndexesToAdd[i] && (len(gandiRecord.Values) == 0 || gandiRecord.Values[0] != currentPublicIPv4) {
configIndexesToAdd[i] = false
addedConfigRecord = true
newRecords = append(newRecords, GandiDomainRecord{
Name: configRecord.Name,
Type: configRecord.Type,
TTL: config.GandiRecordTTLSeconds,
Values: []string{currentPublicIPv4},
})
}
}
}
if !addedConfigRecord {
newRecords = append(newRecords, gandiRecord)
}
}
for i, configRecord := range domain.Records {
if configIndexesToAdd[i] {
newRecords = append([]GandiDomainRecord{{
Name: configRecord.Name,
Type: configRecord.Type,
TTL: config.GandiRecordTTLSeconds,
Values: []string{currentPublicIPv4},
}}, newRecords...)
}
}
requestBody := GandiDomainRecords{
RemoveApexNS: false,
Items: newRecords,
}
requestBodyBytes, err := json.Marshal(requestBody)
if err != nil {
panic(err)
}
log.Printf("replacing all resource records for the domain %s...\n%s\n\n", domain.Domain, string(requestBodyBytes))
endpoint := fmt.Sprintf("/livedns/domains/%s/records", domain.Domain)
statusCode, responseBytes, err = gandi.GandiHTTP("PUT", endpoint, bytes.NewBuffer(requestBodyBytes))
if err != nil {
panic(err)
}
if statusCode >= 300 {
log.Printf("gandi (PUT %s) returned http %d. we PUT the data: %s\n\n and gandi responded with:\n%s\n\n", endpoint, statusCode, string(requestBodyBytes), responseBytes)
}
return true
}
func getConsensusPublicIpv4WithCache(config *Config) (string, error) {

185
porkbun_service.go

@ -0,0 +1,185 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strconv"
"strings"
"time"
errors "git.sequentialread.com/forest/pkg-errors"
)
// https://porkbun.com/api/json/v3/documentation
type PorkbunService struct {
Client *http.Client
APIUrl string
RecordTTLSeconds int
Auth PorkbunAuth
}
type PorkbunAuth struct {
KeyId string `json:"apikey"`
SecretKey string `json:"secretapikey"`
}
type PorkbunRecordsResponse struct {
Records []PorkbunRecord `json:"records"`
}
type PorkbunStatusResponse struct {
Status string `json:"status"`
}
type PorkbunRecord struct {
Id string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL string `json:"ttl"`
Priority string `json:"prio"`
Notes string `json:"notes"`
}
type PorkbunUpdate struct {
KeyId string `json:"apikey"`
SecretKey string `json:"secretapikey"`
Name string `json:"name"`
Type string `json:"type"`
Content string `json:"content"`
TTL string `json:"ttl"`
Priority string `json:"prio"`
}
func NewPorkbunService(keyId, secretKey string, recordTTLSeconds int) *PorkbunService {
return &PorkbunService{
Client: &http.Client{Timeout: 30 * time.Second},
APIUrl: "https://porkbun.com/api/json/v3",
Auth: PorkbunAuth{
KeyId: keyId,
SecretKey: secretKey,
},
RecordTTLSeconds: recordTTLSeconds,
}
}
func (service *PorkbunService) TryToUpdateRecordsForDomain(domain DomainConfig, currentPublicIPv4 string) bool {
path := fmt.Sprintf("/dns/retrieve/%s", domain.Domain)
authJsonBytes, _ := json.Marshal(service.Auth)
statusCode, responseBytes, err := service.PorkbunHTTP("POST", path, bytes.NewBuffer(authJsonBytes))
if err != nil {
// if porkbun isn't responding at all, we can just give up and try again in 10 seconds.
log.Println("waiting for porkbun to respond...")
return false
}
if statusCode >= 300 {
// if we got a non-200 status code from gandi, thats a fatal error. Log it.
log.Printf("porkbun (GET %s) returned http %d: \n%s\n\n", path, statusCode, responseBytes)
return false
}
var responseObj PorkbunRecordsResponse
err = json.Unmarshal(responseBytes, &responseObj)
if err != nil {
log.Printf("porkbun (GET %s) returned http %d, but it didn't return valid json: %s: \n%s\n\n", path, statusCode, err, responseBytes)
return false
}
configIndexesToAdd := map[int]bool{}
for i, _ := range domain.Records {
configIndexesToAdd[i] = true
}
for _, porkbunRecord := range responseObj.Records {
for i, configRecord := range domain.Records {
if configRecord.FQDN(domain.Domain) == porkbunRecord.Name && recordTypesMatch(configRecord.Type, porkbunRecord.Type) {
configIndexesToAdd[i] = false
if porkbunRecord.Content != currentPublicIPv4 {
updateJsonBytes, _ := json.Marshal(PorkbunUpdate{
KeyId: service.Auth.KeyId,
SecretKey: service.Auth.SecretKey,
Name: strings.ReplaceAll(configRecord.Name, "@", ""),
Type: configRecord.Type,
TTL: strconv.Itoa(service.RecordTTLSeconds),
Content: currentPublicIPv4,
})
path := fmt.Sprintf("/dns/edit/%s/%s", domain.Domain, porkbunRecord.Id)
log.Printf("porkbun POST %s\n", path)
statusCode, responseBytes, err := service.PorkbunHTTP("POST", path, bytes.NewBuffer(updateJsonBytes))
if err != nil {
log.Println("waiting for porkbun to respond...")
return false
}
var responseObj PorkbunStatusResponse
err = json.Unmarshal(responseBytes, &responseObj)
if err != nil {
log.Printf("porkbun (POST %s) returned http %d, but it didn't return valid json: %s: \n%s\n\n", path, statusCode, err, responseBytes)
return false
}
if responseObj.Status != "SUCCESS" {
log.Printf("porkbun (POST %s) returned http %d: \n%s\n\n", path, statusCode, responseBytes)
return false
}
}
}
}
}
for i, configRecord := range domain.Records {
if configIndexesToAdd[i] {
createJsonBytes, _ := json.Marshal(PorkbunUpdate{
KeyId: service.Auth.KeyId,
SecretKey: service.Auth.SecretKey,
Name: strings.ReplaceAll(configRecord.Name, "@", ""),
Type: configRecord.Type,
TTL: strconv.Itoa(service.RecordTTLSeconds),
Content: currentPublicIPv4,
})
path := fmt.Sprintf("/dns/create/%s", domain.Domain)
log.Printf("porkbun POST %s\n", path)
statusCode, responseBytes, err := service.PorkbunHTTP("POST", path, bytes.NewBuffer(createJsonBytes))
if err != nil {
log.Println("waiting for porkbun to respond...")
return false
}
var responseObj PorkbunStatusResponse
err = json.Unmarshal(responseBytes, &responseObj)
if err != nil {
log.Printf("porkbun (POST %s) returned http %d, but it didn't return valid json: %s: \n%s\n\n", path, statusCode, err, responseBytes)
return false
}
if responseObj.Status != "SUCCESS" {
log.Printf("porkbun (POST %s) returned http %d: \n%s\n\n", path, statusCode, responseBytes)
return false
}
}
}
return true
}
func (service *PorkbunService) PorkbunHTTP(
method string,
url string,
body io.Reader,
) (int, []byte, error) {
request, err := http.NewRequest(method, url, body)
if err != nil {
return 0, nil, errors.Wrapf(err, "failed to create HTTP request calling %s %s", method, url)
}
response, err := service.Client.Do(request)
if err != nil {
return 0, nil, errors.Wrapf(err, "HTTP request error when calling %s %s", method, url)
}
bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return response.StatusCode, nil, errors.Wrapf(err, "HTTP read error when calling %s %s ", method, url)
}
return response.StatusCode, bytes, err
}
Loading…
Cancel
Save