Procházet zdrojové kódy

migrate bind -> powerdns

Alexander Belanger před 4 roky
rodič
revize
ef579e4c79

+ 1 - 1
api/server/handlers/release/create_subdomain.go

@@ -67,7 +67,7 @@ func (c *CreateSubdomainHandler) ServeHTTP(w http.ResponseWriter, r *http.Reques
 
 	_record := domain.DNSRecord(*record)
 
-	err = _record.CreateDomain(c.Config().BindClient)
+	err = _record.CreateDomain(c.Config().PowerDNSClient)
 
 	if err != nil {
 		c.HandleAPIError(w, r, apierrors.NewErrInternal(err))

+ 3 - 3
api/server/shared/config/config.go

@@ -8,7 +8,7 @@ import (
 	"github.com/porter-dev/porter/internal/analytics"
 	"github.com/porter-dev/porter/internal/auth/token"
 	"github.com/porter-dev/porter/internal/helm/urlcache"
-	"github.com/porter-dev/porter/internal/integrations/bind"
+	"github.com/porter-dev/porter/internal/integrations/powerdns"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/logger"
 	"github.com/porter-dev/porter/internal/notifier"
@@ -83,8 +83,8 @@ type Config struct {
 	// AnalyticsClient if Segment analytics reporting is enabled on the API instance
 	AnalyticsClient analytics.AnalyticsSegmentClient
 
-	// BindClient is a client for Bind DNS, if the Porter instance supports vanity URLs
-	BindClient *bind.Client
+	// PowerDNSClient is a client for PowerDNS, if the Porter instance supports vanity URLs
+	PowerDNSClient *powerdns.Client
 }
 
 type ConfigLoader interface {

+ 3 - 3
api/server/shared/config/env/envconfs.go

@@ -55,9 +55,9 @@ type ServerConf struct {
 	ProvisionerImagePullSecret string `env:"PROV_IMAGE_PULL_SECRET"`
 	SegmentClientKey           string `env:"SEGMENT_CLIENT_KEY"`
 
-	// Bind client API key and the host of the Bind DNS API server
-	BindAPIServerURL string `env:"BIND_API_SERVER_URL"`
-	BindAPIKey       string `env:"BIND_API_KEY"`
+	// PowerDNS client API key and the host of the PowerDNS API server
+	PowerDNSAPIServerURL string `env:"POWER_DNS_API_SERVER_URL"`
+	PowerDNSAPIKey       string `env:"POWER_DNS_API_KEY"`
 
 	// Email for an admin user. On a self-hosted instance of Porter, the
 	// admin user is the only user that can log in and register. After the admin

+ 3 - 3
api/server/shared/config/loader/loader.go

@@ -15,7 +15,7 @@ import (
 	"github.com/porter-dev/porter/internal/auth/sessionstore"
 	"github.com/porter-dev/porter/internal/auth/token"
 	"github.com/porter-dev/porter/internal/helm/urlcache"
-	"github.com/porter-dev/porter/internal/integrations/bind"
+	"github.com/porter-dev/porter/internal/integrations/powerdns"
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/kubernetes/local"
 	"github.com/porter-dev/porter/internal/notifier"
@@ -193,8 +193,8 @@ func (e *EnvConfigLoader) LoadConfig() (res *config.Config, err error) {
 
 	res.AnalyticsClient = analytics.InitializeAnalyticsSegmentClient(sc.SegmentClientKey, res.Logger)
 
-	if sc.BindAPIKey != "" && sc.BindAPIServerURL != "" {
-		res.BindClient = bind.NewClient(sc.BindAPIServerURL, sc.BindAPIKey)
+	if sc.PowerDNSAPIKey != "" && sc.PowerDNSAPIServerURL != "" {
+		res.PowerDNSClient = powerdns.NewClient(sc.PowerDNSAPIServerURL, sc.PowerDNSAPIKey, sc.AppRootDomain)
 	}
 
 	return res, nil

+ 62 - 23
internal/integrations/bind/bind.go → internal/integrations/powerdns/powerdns.go

@@ -1,4 +1,4 @@
-package bind
+package powerdns
 
 import (
 	"encoding/json"
@@ -10,57 +10,96 @@ import (
 	"time"
 )
 
-// Client contains an API client for a Bind DNS server wrapped
-// with a lightweight API
+// Client contains an API client for a PowerDNS server
 type Client struct {
 	apiKey    string
 	serverURL string
+	runDomain string
 
 	httpClient *http.Client
 }
 
 // NewClient creates a new bind API client
-func NewClient(serverURL, apiKey string) *Client {
+func NewClient(serverURL, apiKey, runDomain string) *Client {
 	httpClient := &http.Client{
 		Timeout: time.Minute,
 	}
 
-	return &Client{apiKey, serverURL, httpClient}
+	return &Client{apiKey, serverURL, runDomain, httpClient}
 }
 
 // RecordData represents the data required to create or delete an A/CNAME record
 // for the nameserver
 type RecordData struct {
-	Value    string `json:"value"`
+	RRSets []RR `json:"rrsets"`
+}
+
+type RR struct {
+	Name       string   `json:"name"`
+	Type       string   `json:"type"`
+	ChangeType string   `json:"changetype"`
+	TTL        uint     `json:"ttl"`
+	Records    []Record `json:"record"`
+}
+
+type Record struct {
+	Content  string `json:"content"`
+	Disabled bool   `json:"disabled"`
+	Name     string `json:"name"`
 	Type     string `json:"type"`
-	Hostname string `json:"hostname"`
+	Priority uint   `json:"priority"`
 }
 
 // CreateCNAMERecord creates a new CNAME record for the nameserver
 func (c *Client) CreateCNAMERecord(value, hostname string) error {
-	return c.sendRequest("POST", &RecordData{
-		Value:    value,
-		Type:     "CNAME",
-		Hostname: hostname,
+	valueC := canonicalize(value)
+	hostnameC := canonicalize(hostname)
+
+	return c.sendRequest("PATCH", &RecordData{
+		RRSets: []RR{{
+			Name:       hostnameC,
+			Type:       "CNAME",
+			ChangeType: "REPLACE",
+			TTL:        300,
+			Records: []Record{{
+				Content:  valueC,
+				Disabled: false,
+				Name:     hostnameC,
+				Type:     "CNAME",
+				Priority: 0,
+			}},
+		}},
 	})
 }
 
 // CreateARecord creates a new A record for the nameserver
 func (c *Client) CreateARecord(value, hostname string) error {
-	return c.sendRequest("POST", &RecordData{
-		Value:    value,
-		Type:     "A",
-		Hostname: hostname,
+	hostnameC := canonicalize(hostname)
+
+	return c.sendRequest("PATCH", &RecordData{
+		RRSets: []RR{{
+			Name:       hostnameC,
+			Type:       "A",
+			ChangeType: "REPLACE",
+			TTL:        300,
+			Records: []Record{{
+				Content:  value,
+				Disabled: false,
+				Name:     hostnameC,
+				Type:     "A",
+				Priority: 0,
+			}},
+		}},
 	})
 }
 
-// DeleteARecord deletes a new A record for the nameserver
-func (c *Client) DeleteARecord(value, hostname string) error {
-	return c.sendRequest("DELETE", &RecordData{
-		Value:    value,
-		Type:     "A",
-		Hostname: hostname,
-	})
+func canonicalize(value string) string {
+	// if the string ends in a period, return
+	if value[len(value)-1:] == "." {
+		return value
+	}
+
+	return fmt.Sprintf("%s.", value)
 }
 
 func (c *Client) sendRequest(method string, data *RecordData) error {
@@ -70,7 +109,7 @@ func (c *Client) sendRequest(method string, data *RecordData) error {
 		return nil
 	}
 
-	reqURL.Path = "/dns"
+	reqURL.Path = fmt.Sprintf("/api/v1/servers/localhost/zones/%s", c.runDomain)
 
 	strData, err := json.Marshal(data)
 

+ 4 - 4
internal/kubernetes/domain/domain.go

@@ -6,7 +6,7 @@ import (
 	"net"
 	"strings"
 
-	"github.com/porter-dev/porter/internal/integrations/bind"
+	"github.com/porter-dev/porter/internal/integrations/powerdns"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
 	v1 "k8s.io/api/core/v1"
@@ -80,13 +80,13 @@ func (c *CreateDNSRecordConfig) NewDNSRecordForEndpoint() *models.DNSRecord {
 }
 
 // CreateDomain creates a new record for the vanity domain
-func (e *DNSRecord) CreateDomain(bindClient *bind.Client) error {
+func (e *DNSRecord) CreateDomain(powerDNSClient *powerdns.Client) error {
 	isIPv4 := net.ParseIP(e.Endpoint) != nil
 	domain := fmt.Sprintf("%s.%s", e.SubdomainPrefix, e.RootDomain)
 
 	if isIPv4 {
-		return bindClient.CreateARecord(e.Endpoint, domain)
+		return powerDNSClient.CreateARecord(e.Endpoint, domain)
 	}
 
-	return bindClient.CreateCNAMERecord(e.Endpoint, domain)
+	return powerDNSClient.CreateCNAMERecord(e.Endpoint, domain)
 }