diff --git a/cattle.go b/cattle.go index 1790638..834fb51 100644 --- a/cattle.go +++ b/cattle.go @@ -1,7 +1,7 @@ package main import ( - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/utils" "github.com/rancher/go-rancher/client" ) @@ -25,7 +25,7 @@ func NewCattleClient(cattleUrl string, cattleAccessKey string, cattleSecretKey s }, nil } -func (c *CattleClient) UpdateServiceDomainName(serviceDnsRecord dns.ServiceDnsRecord) error { +func (c *CattleClient) UpdateServiceDomainName(serviceDnsRecord utils.ServiceDnsRecord) error { event := &client.ExternalDnsEvent{ EventType: "dns.update", diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..75970ed --- /dev/null +++ b/config/config.go @@ -0,0 +1,39 @@ +package config + +import ( + "os" + "strconv" + + "github.com/Sirupsen/logrus" + "github.com/rancher/external-dns/utils" +) + +var ( + RootDomainName string + TTL int + CattleURL string + CattleAccessKey string + CattleSecretKey string +) + +func SetFromEnvironment() { + CattleURL = getEnv("CATTLE_URL") + CattleAccessKey = getEnv("CATTLE_ACCESS_KEY") + CattleSecretKey = getEnv("CATTLE_SECRET_KEY") + RootDomainName = utils.Fqdn(getEnv("ROOT_DOMAIN")) + TTLEnv := os.Getenv("TTL") + i, err := strconv.Atoi(TTLEnv) + if err != nil { + TTL = 300 + } else { + TTL = i + } +} + +func getEnv(name string) string { + envVar := os.Getenv(name) + if len(envVar) == 0 { + logrus.Fatalf("Environment variable '%s' is not set", name) + } + return envVar +} diff --git a/dns/dns.go b/dns/dns.go deleted file mode 100644 index 8a4acfe..0000000 --- a/dns/dns.go +++ /dev/null @@ -1,58 +0,0 @@ -package dns - -import ( - "github.com/Sirupsen/logrus" - "os" - "strconv" - "strings" -) - -var ( - RootDomainName string - TTL int -) - -type DnsRecord struct { - Fqdn string - Records []string - Type string - TTL int -} - -type ServiceDnsRecord struct { - Fqdn string - ServiceName string - StackName string -} - -func init() { - var name string - name = os.Getenv("ROOT_DOMAIN") - if len(name) == 0 { - logrus.Fatalf("ROOT_DOMAIN is not set") - } - TTLEnv := os.Getenv("TTL") - i, err := strconv.Atoi(TTLEnv) - if err != nil { - TTL = 300 - } else { - TTL = i - } - - if !strings.HasSuffix(name, ".") { - name = name + "." - } - - RootDomainName = name -} - -func ConvertToServiceDnsRecord(dnsRecord DnsRecord) ServiceDnsRecord { - splitted := strings.Split(dnsRecord.Fqdn, ".") - serviceRecord := ServiceDnsRecord{dnsRecord.Fqdn, splitted[0], splitted[1]} - return serviceRecord -} - -func ConvertToFqdn(serviceName string, stackName string, environmentName string) string { - domainNameEntries := []string{serviceName, stackName, environmentName, RootDomainName} - return strings.ToLower(strings.Join(domainNameEntries, ".")) -} diff --git a/external-dns.go b/external-dns.go index a808e40..737677b 100644 --- a/external-dns.go +++ b/external-dns.go @@ -3,12 +3,13 @@ package main import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/config" + "github.com/rancher/external-dns/utils" "strings" ) -func UpdateProviderDnsRecords(metadataRecs map[string]dns.DnsRecord) ([]dns.DnsRecord, error) { - var updated []dns.DnsRecord +func UpdateProviderDnsRecords(metadataRecs map[string]utils.DnsRecord) ([]utils.DnsRecord, error) { + var updated []utils.DnsRecord providerRecs, err := getProviderDnsRecords() if err != nil { return nil, fmt.Errorf("Provider error reading dns entries: %v", err) @@ -24,8 +25,8 @@ func UpdateProviderDnsRecords(metadataRecs map[string]dns.DnsRecord) ([]dns.DnsR return updated, nil } -func addMissingRecords(metadataRecs map[string]dns.DnsRecord, providerRecs map[string]dns.DnsRecord) []dns.DnsRecord { - var toAdd []dns.DnsRecord +func addMissingRecords(metadataRecs map[string]utils.DnsRecord, providerRecs map[string]utils.DnsRecord) []utils.DnsRecord { + var toAdd []utils.DnsRecord for key := range metadataRecs { if _, ok := providerRecs[key]; !ok { toAdd = append(toAdd, metadataRecs[key]) @@ -39,8 +40,8 @@ func addMissingRecords(metadataRecs map[string]dns.DnsRecord, providerRecs map[s return updateRecords(toAdd, &Add) } -func updateRecords(toChange []dns.DnsRecord, op *Op) []dns.DnsRecord { - var changed []dns.DnsRecord +func updateRecords(toChange []utils.DnsRecord, op *Op) []utils.DnsRecord { + var changed []utils.DnsRecord for _, value := range toChange { switch *op { case Add: @@ -67,8 +68,8 @@ func updateRecords(toChange []dns.DnsRecord, op *Op) []dns.DnsRecord { return changed } -func updateExistingRecords(metadataRecs map[string]dns.DnsRecord, providerRecs map[string]dns.DnsRecord) []dns.DnsRecord { - var toUpdate []dns.DnsRecord +func updateExistingRecords(metadataRecs map[string]utils.DnsRecord, providerRecs map[string]utils.DnsRecord) []utils.DnsRecord { + var toUpdate []utils.DnsRecord for key := range metadataRecs { if _, ok := providerRecs[key]; ok { metadataR := make(map[string]struct{}, len(metadataRecs[key].Records)) @@ -110,8 +111,8 @@ func updateExistingRecords(metadataRecs map[string]dns.DnsRecord, providerRecs m return updateRecords(toUpdate, &Update) } -func removeExtraRecords(metadataRecs map[string]dns.DnsRecord, providerRecs map[string]dns.DnsRecord) []dns.DnsRecord { - var toRemove []dns.DnsRecord +func removeExtraRecords(metadataRecs map[string]utils.DnsRecord, providerRecs map[string]utils.DnsRecord) []utils.DnsRecord { + var toRemove []utils.DnsRecord for key := range providerRecs { if _, ok := metadataRecs[key]; !ok { toRemove = append(toRemove, providerRecs[key]) @@ -126,16 +127,16 @@ func removeExtraRecords(metadataRecs map[string]dns.DnsRecord, providerRecs map[ return updateRecords(toRemove, &Remove) } -func getProviderDnsRecords() (map[string]dns.DnsRecord, error) { +func getProviderDnsRecords() (map[string]utils.DnsRecord, error) { allRecords, err := provider.GetRecords() if err != nil { return nil, err } - ourRecords := make(map[string]dns.DnsRecord, len(allRecords)) - joins := []string{m.EnvironmentName, dns.RootDomainName} + ourRecords := make(map[string]utils.DnsRecord, len(allRecords)) + joins := []string{m.EnvironmentName, config.RootDomainName} suffix := "." + strings.ToLower(strings.Join(joins, ".")) for _, value := range allRecords { - if value.Type == "A" && strings.HasSuffix(value.Fqdn, suffix) && value.TTL == dns.TTL { + if value.Type == "A" && strings.HasSuffix(value.Fqdn, suffix) && value.TTL == config.TTL { ourRecords[value.Fqdn] = value } } diff --git a/healthcheck.go b/healthcheck.go index 031b21c..c7adb5e 100644 --- a/healthcheck.go +++ b/healthcheck.go @@ -25,9 +25,9 @@ func healtcheck(w http.ResponseWriter, req *http.Request) { http.Error(w, "Failed to reach metadata server", http.StatusInternalServerError) } else { // 2) test provider - _, err := provider.GetRecords() + err := provider.HealthCheck() if err != nil { - logrus.Error("Healtcheck failed: unable to reach a provider") + logrus.Error("Healtcheck failed: Error from provider: %v", err) http.Error(w, "Failed to reach an external provider ", http.StatusInternalServerError) } else { err := c.TestConnect() diff --git a/main.go b/main.go index 4b4800a..539482c 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,21 @@ package main import ( "flag" + "os" + "time" + "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/config" "github.com/rancher/external-dns/metadata" "github.com/rancher/external-dns/providers" - "os" - "time" + _ "github.com/rancher/external-dns/providers/cloudflare" + _ "github.com/rancher/external-dns/providers/dnsimple" + _ "github.com/rancher/external-dns/providers/gandi" + _ "github.com/rancher/external-dns/providers/pointhq" + _ "github.com/rancher/external-dns/providers/powerdns" + _ "github.com/rancher/external-dns/providers/rfc2136" + _ "github.com/rancher/external-dns/providers/route53" + "github.com/rancher/external-dns/utils" ) const ( @@ -27,7 +36,7 @@ var ( ) var ( - providerName = flag.String("provider", "", "External provider name") + providerName = flag.String("provider", "route53", "External provider name") debug = flag.Bool("debug", false, "Debug") logFile = flag.String("log", "", "Log file") @@ -38,7 +47,6 @@ var ( func setEnv() { flag.Parse() - provider = providers.GetProvider(*providerName) if *debug { logrus.SetLevel(logrus.DebugLevel) } @@ -54,39 +62,32 @@ func setEnv() { } } + // get config from environment variables + config.SetFromEnvironment() + + var err error // configure metadata client - mClient, err := metadata.NewMetadataClient() + m, err = metadata.NewMetadataClient() if err != nil { logrus.Fatalf("Failed to configure rancher-metadata client: %v", err) } - m = mClient - - cattleUrl := os.Getenv("CATTLE_URL") - if len(cattleUrl) == 0 { - logrus.Fatalf("CATTLE_URL is not set") - } - - cattleApiKey := os.Getenv("CATTLE_ACCESS_KEY") - if len(cattleApiKey) == 0 { - logrus.Fatalf("CATTLE_ACCESS_KEY is not set") - } - - cattleSecretKey := os.Getenv("CATTLE_SECRET_KEY") - if len(cattleSecretKey) == 0 { - logrus.Fatalf("CATTLE_SECRET_KEY is not set") - } //configure cattle client - c, err = NewCattleClient(cattleUrl, cattleApiKey, cattleSecretKey) + c, err = NewCattleClient(config.CattleURL, config.CattleAccessKey, config.CattleSecretKey) if err != nil { logrus.Fatalf("Failed to configure cattle client: %v", err) } + + // get provider + provider, err = providers.GetProvider(*providerName, config.RootDomainName) + if err != nil { + logrus.Fatalf("Failed to get provider '%s': %v", *providerName, err) + } } func main() { logrus.Infof("Starting Rancher External DNS service") setEnv() - logrus.Infof("Powered by %s", provider.GetName()) go startHealthcheck() @@ -125,7 +126,7 @@ func main() { } for _, toUpdate := range updated { - serviceDnsRecord := dns.ConvertToServiceDnsRecord(toUpdate) + serviceDnsRecord := utils.ConvertToServiceDnsRecord(toUpdate) c.UpdateServiceDomainName(serviceDnsRecord) } diff --git a/metadata/metadata.go b/metadata/metadata.go index b3d3c47..64b64b9 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -3,7 +3,8 @@ package metadata import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/config" + "github.com/rancher/external-dns/utils" "github.com/rancher/go-rancher-metadata/metadata" "time" ) @@ -54,8 +55,8 @@ func (m *MetadataClient) GetVersion() (string, error) { return m.MetadataClient.GetVersion() } -func (m *MetadataClient) GetMetadataDnsRecords() (map[string]dns.DnsRecord, error) { - dnsEntries := make(map[string]dns.DnsRecord) +func (m *MetadataClient) GetMetadataDnsRecords() (map[string]utils.DnsRecord, error) { + dnsEntries := make(map[string]utils.DnsRecord) err := m.getContainersDnsRecords(dnsEntries, "", "") if err != nil { return dnsEntries, err @@ -63,7 +64,7 @@ func (m *MetadataClient) GetMetadataDnsRecords() (map[string]dns.DnsRecord, erro return dnsEntries, nil } -func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]dns.DnsRecord, serviceName string, stackName string) error { +func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]utils.DnsRecord, serviceName string, stackName string) error { containers, err := m.MetadataClient.GetContainers() if err != nil { return err @@ -104,9 +105,9 @@ func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]dns.DnsRe ip = host.AgentIP } - fqdn := dns.ConvertToFqdn(container.ServiceName, container.StackName, m.EnvironmentName) + fqdn := utils.ConvertToFqdn(container.ServiceName, container.StackName, m.EnvironmentName, config.RootDomainName) records := []string{ip} - dnsEntry := dns.DnsRecord{fqdn, records, "A", dns.TTL} + dnsEntry := utils.DnsRecord{fqdn, records, "A", config.TTL} addToDnsEntries(dnsEntry, dnsEntries) } @@ -114,7 +115,7 @@ func (m *MetadataClient) getContainersDnsRecords(dnsEntries map[string]dns.DnsRe return nil } -func addToDnsEntries(dnsEntry dns.DnsRecord, dnsEntries map[string]dns.DnsRecord) { +func addToDnsEntries(dnsEntry utils.DnsRecord, dnsEntries map[string]utils.DnsRecord) { var records []string if _, ok := dnsEntries[dnsEntry.Fqdn]; !ok { records = dnsEntry.Records @@ -122,6 +123,6 @@ func addToDnsEntries(dnsEntry dns.DnsRecord, dnsEntries map[string]dns.DnsRecord records = dnsEntries[dnsEntry.Fqdn].Records records = append(records, dnsEntry.Records...) } - dnsEntry = dns.DnsRecord{dnsEntry.Fqdn, records, "A", dns.TTL} + dnsEntry = utils.DnsRecord{dnsEntry.Fqdn, records, "A", config.TTL} dnsEntries[dnsEntry.Fqdn] = dnsEntry } diff --git a/providers/cloudflare.go b/providers/cloudflare/cloudflare.go similarity index 55% rename from providers/cloudflare.go rename to providers/cloudflare/cloudflare.go index 00286f3..67f1402 100644 --- a/providers/cloudflare.go +++ b/providers/cloudflare/cloudflare.go @@ -1,61 +1,63 @@ -package providers +package cloudflare import ( "fmt" "os" "github.com/Sirupsen/logrus" - "github.com/crackcomm/cloudflare" - "github.com/rancher/external-dns/dns" + api "github.com/crackcomm/cloudflare" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" "golang.org/x/net/context" ) -type CloudflareHandler struct { - client *cloudflare.Client - zone *cloudflare.Zone +type CloudflareProvider struct { + client *api.Client + zone *api.Zone ctx context.Context root string } func init() { - cloudflareHandler := &CloudflareHandler{} - - email := os.Getenv("CLOUDFLARE_EMAIL") - if len(email) == 0 { - logrus.Infof("CLOUDFLARE_EMAIL is not set, skipping init of %s provider", cloudflareHandler.GetName()) - return - } + providers.RegisterProvider("cloudflare", &CloudflareProvider{}) +} - apiKey := os.Getenv("CLOUDFLARE_KEY") - if len(apiKey) == 0 { - logrus.Infof("CLOUDFLARE_KEY is not set, skipping init of %s provider", cloudflareHandler.GetName()) - return +func (c *CloudflareProvider) Init(rootDomainName string) error { + var email, apiKey string + if email = os.Getenv("CLOUDFLARE_EMAIL"); len(email) == 0 { + return fmt.Errorf("CLOUDFLARE_EMAIL is not set") } - if err := RegisterProvider("cloudflare", cloudflareHandler); err != nil { - logrus.Fatal("Could not register cloudflare provider") + if apiKey = os.Getenv("CLOUDFLARE_KEY"); len(apiKey) == 0 { + return fmt.Errorf("CLOUDFLARE_KEY is not set") } - cloudflareHandler.client = cloudflare.New(&cloudflare.Options{ + c.client = api.New(&api.Options{ Email: email, Key: apiKey, }) - cloudflareHandler.ctx = context.Background() - cloudflareHandler.root = unFqdn(dns.RootDomainName) + c.ctx = context.Background() + c.root = utils.UnFqdn(rootDomainName) - if err := cloudflareHandler.setZone(); err != nil { - logrus.Fatalf("Failed to set zone for root domain %s: %v", cloudflareHandler.root, err) + if err := c.setZone(); err != nil { + return fmt.Errorf("Failed to set zone for root domain %s: %v", c.root, err) } - logrus.Infof("Configured %s with zone \"%s\" ", cloudflareHandler.GetName(), cloudflareHandler.root) + logrus.Infof("Configured %s with zone '%s'", c.GetName(), c.root) + return nil } -func (*CloudflareHandler) GetName() string { +func (*CloudflareProvider) GetName() string { return "CloudFlare" } -func (c *CloudflareHandler) AddRecord(record dns.DnsRecord) error { +func (c *CloudflareProvider) HealthCheck() error { + _, err := c.client.Zones.Details(c.ctx, c.zone.ID) + return err +} + +func (c *CloudflareProvider) AddRecord(record utils.DnsRecord) error { for _, rec := range record.Records { r := c.prepareRecord(record) r.Content = rec @@ -68,7 +70,7 @@ func (c *CloudflareHandler) AddRecord(record dns.DnsRecord) error { return nil } -func (c *CloudflareHandler) UpdateRecord(record dns.DnsRecord) error { +func (c *CloudflareProvider) UpdateRecord(record utils.DnsRecord) error { if err := c.RemoveRecord(record); err != nil { return err } @@ -76,7 +78,7 @@ func (c *CloudflareHandler) UpdateRecord(record dns.DnsRecord) error { return c.AddRecord(record) } -func (c *CloudflareHandler) RemoveRecord(record dns.DnsRecord) error { +func (c *CloudflareProvider) RemoveRecord(record utils.DnsRecord) error { records, err := c.findRecords(record) if err != nil { return err @@ -92,8 +94,8 @@ func (c *CloudflareHandler) RemoveRecord(record dns.DnsRecord) error { return nil } -func (c *CloudflareHandler) GetRecords() ([]dns.DnsRecord, error) { - var records []dns.DnsRecord +func (c *CloudflareProvider) GetRecords() ([]utils.DnsRecord, error) { + var records []utils.DnsRecord result, err := c.client.Records.List(c.ctx, c.zone.ID) if err != nil { return records, fmt.Errorf("CloudFlare API call has failed: %v", err) @@ -103,7 +105,7 @@ func (c *CloudflareHandler) GetRecords() ([]dns.DnsRecord, error) { recordTTLs := map[string]map[string]int{} for _, rec := range result { - fqdn := fqdn(rec.Name) + fqdn := utils.Fqdn(rec.Name) recordTTLs[fqdn] = map[string]int{} recordTTLs[fqdn][rec.Type] = rec.TTL recordSet, exists := recordMap[fqdn] @@ -124,14 +126,15 @@ func (c *CloudflareHandler) GetRecords() ([]dns.DnsRecord, error) { for fqdn, recordSet := range recordMap { for recordType, recordSlice := range recordSet { ttl := recordTTLs[fqdn][recordType] - record := dns.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} + record := utils.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} records = append(records, record) } } + return records, nil } -func (c *CloudflareHandler) setZone() error { +func (c *CloudflareProvider) setZone() error { zones, err := c.client.Zones.List(c.ctx) if err != nil { return fmt.Errorf("CloudFlare API call has failed: %v", err) @@ -150,9 +153,9 @@ func (c *CloudflareHandler) setZone() error { return nil } -func (c *CloudflareHandler) prepareRecord(record dns.DnsRecord) *cloudflare.Record { - name := unFqdn(record.Fqdn) - return &cloudflare.Record{ +func (c *CloudflareProvider) prepareRecord(record utils.DnsRecord) *api.Record { + name := utils.UnFqdn(record.Fqdn) + return &api.Record{ Type: record.Type, Name: name, TTL: sanitizeTTL(record.TTL), @@ -160,14 +163,14 @@ func (c *CloudflareHandler) prepareRecord(record dns.DnsRecord) *cloudflare.Reco } } -func (c *CloudflareHandler) findRecords(record dns.DnsRecord) ([]*cloudflare.Record, error) { - var records []*cloudflare.Record +func (c *CloudflareProvider) findRecords(record utils.DnsRecord) ([]*api.Record, error) { + var records []*api.Record result, err := c.client.Records.List(c.ctx, c.zone.ID) if err != nil { return records, fmt.Errorf("CloudFlare API call has failed: %v", err) } - name := unFqdn(record.Fqdn) + name := utils.UnFqdn(record.Fqdn) for _, rec := range result { if rec.Name == name && rec.Type == record.Type { records = append(records, rec) @@ -186,19 +189,3 @@ func sanitizeTTL(ttl int) int { } return ttl } - -func fqdn(name string) string { - n := len(name) - if n == 0 || name[n-1] == '.' { - return name - } - return name + "." -} - -func unFqdn(name string) string { - n := len(name) - if n != 0 && name[n-1] == '.' { - return name[:n-1] - } - return name -} diff --git a/providers/dnsimple.go b/providers/dnsimple/dnsimple.go similarity index 56% rename from providers/dnsimple.go rename to providers/dnsimple/dnsimple.go index 6c72a12..e9aa9cf 100644 --- a/providers/dnsimple.go +++ b/providers/dnsimple/dnsimple.go @@ -1,74 +1,77 @@ -package providers +package dnsimple import ( "fmt" - "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" - "github.com/weppos/go-dnsimple/dnsimple" "os" "strings" + + "github.com/Sirupsen/logrus" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" + api "github.com/weppos/go-dnsimple/dnsimple" ) +type DNSimpleProvider struct { + client *api.Client + root string +} + func init() { - apiToken := os.Getenv("DNSIMPLE_TOKEN") - if len(apiToken) == 0 { - logrus.Info("DNSIMPLE_TOKEN is not set, skipping init of DNSimple provider") - return - } + providers.RegisterProvider("dnsimple", &DNSimpleProvider{}) +} - email := os.Getenv("DNSIMPLE_EMAIL") - if len(email) == 0 { - logrus.Info("DNSIMPLE_EMAIL is not set, skipping init of DNSimple provider") - return +func (d *DNSimpleProvider) Init(rootDomainName string) error { + var email, apiToken string + if email = os.Getenv("DNSIMPLE_EMAIL"); len(email) == 0 { + return fmt.Errorf("DNSIMPLE_EMAIL is not set") } - dnsimpleHandler := &DNSimpleHandler{} - if err := RegisterProvider("dnsimple", dnsimpleHandler); err != nil { - logrus.Fatal("Could not register dnsimple provider") + if apiToken = os.Getenv("DNSIMPLE_TOKEN"); len(apiToken) == 0 { + return fmt.Errorf("DNSIMPLE_TOKEN is not set") } - dnsimpleHandler.root = strings.TrimSuffix(dns.RootDomainName, ".") - dnsimpleHandler.client = dnsimple.NewClient(apiToken, email) + d.root = utils.UnFqdn(rootDomainName) + d.client = api.NewClient(apiToken, email) - domains, _, err := dnsimpleHandler.client.Domains.List() + domains, _, err := d.client.Domains.List() if err != nil { - logrus.Fatalf("Failed to list hosted zones: %v", err) + return fmt.Errorf("Failed to list zones: %v", err) } found := false for _, domain := range domains { - if domain.Name == dnsimpleHandler.root { + if domain.Name == d.root { found = true break } } if !found { - logrus.Fatalf("Hosted zone %s is missing", dnsimpleHandler.root) + return fmt.Errorf("Zone for '%s' not found", d.root) } - logrus.Infof("Configured %s with hosted zone %q ", dnsimpleHandler.GetName(), dnsimpleHandler.root) + logrus.Infof("Configured %s with zone '%s'", d.GetName(), d.root) + return nil } -type DNSimpleHandler struct { - client *dnsimple.Client - root string +func (*DNSimpleProvider) GetName() string { + return "DNSimple" } -func (*DNSimpleHandler) GetName() string { - return "DNSimple" +func (d *DNSimpleProvider) HealthCheck() error { + _, _, err := d.client.Users.User() + return err } -func (d *DNSimpleHandler) parseName(record dns.DnsRecord) string { +func (d *DNSimpleProvider) parseName(record utils.DnsRecord) string { name := strings.TrimSuffix(record.Fqdn, fmt.Sprintf(".%s.", d.root)) - return name } -func (d *DNSimpleHandler) AddRecord(record dns.DnsRecord) error { +func (d *DNSimpleProvider) AddRecord(record utils.DnsRecord) error { name := d.parseName(record) for _, rec := range record.Records { - recordInput := dnsimple.Record{ + recordInput := api.Record{ Name: name, TTL: record.TTL, Type: record.Type, @@ -83,8 +86,8 @@ func (d *DNSimpleHandler) AddRecord(record dns.DnsRecord) error { return nil } -func (d *DNSimpleHandler) findRecords(record dns.DnsRecord) ([]dnsimple.Record, error) { - var records []dnsimple.Record +func (d *DNSimpleProvider) findRecords(record utils.DnsRecord) ([]api.Record, error) { + var records []api.Record resp, _, err := d.client.Domains.ListRecords(d.root, "", "") if err != nil { return records, fmt.Errorf("DNSimple API call has failed: %v", err) @@ -100,7 +103,7 @@ func (d *DNSimpleHandler) findRecords(record dns.DnsRecord) ([]dnsimple.Record, return records, nil } -func (d *DNSimpleHandler) UpdateRecord(record dns.DnsRecord) error { +func (d *DNSimpleProvider) UpdateRecord(record utils.DnsRecord) error { err := d.RemoveRecord(record) if err != nil { return err @@ -109,7 +112,7 @@ func (d *DNSimpleHandler) UpdateRecord(record dns.DnsRecord) error { return d.AddRecord(record) } -func (d *DNSimpleHandler) RemoveRecord(record dns.DnsRecord) error { +func (d *DNSimpleProvider) RemoveRecord(record utils.DnsRecord) error { records, err := d.findRecords(record) if err != nil { return err @@ -125,8 +128,8 @@ func (d *DNSimpleHandler) RemoveRecord(record dns.DnsRecord) error { return nil } -func (d *DNSimpleHandler) GetRecords() ([]dns.DnsRecord, error) { - var records []dns.DnsRecord +func (d *DNSimpleProvider) GetRecords() ([]utils.DnsRecord, error) { + var records []utils.DnsRecord recordResp, _, err := d.client.Domains.ListRecords(d.root, "", "") if err != nil { @@ -139,9 +142,9 @@ func (d *DNSimpleHandler) GetRecords() ([]dns.DnsRecord, error) { for _, rec := range recordResp { var fqdn string if rec.Name == "" { - fqdn = dns.RootDomainName + fqdn = d.root + "." } else { - fqdn = fmt.Sprintf("%s.%s", rec.Name, dns.RootDomainName) + fqdn = fmt.Sprintf("%s.%s.", rec.Name, d.root) } recordTTLs[fqdn] = map[string]int{} @@ -164,7 +167,7 @@ func (d *DNSimpleHandler) GetRecords() ([]dns.DnsRecord, error) { for fqdn, recordSet := range recordMap { for recordType, recordSlice := range recordSet { ttl := recordTTLs[fqdn][recordType] - record := dns.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} + record := utils.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} records = append(records, record) } } diff --git a/providers/gandi.go b/providers/gandi/gandi.go similarity index 63% rename from providers/gandi.go rename to providers/gandi/gandi.go index 94abf39..63ab6f1 100644 --- a/providers/gandi.go +++ b/providers/gandi/gandi.go @@ -1,4 +1,4 @@ -package providers +package gandi import ( "fmt" @@ -6,19 +6,22 @@ import ( "strings" "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" - gandi "github.com/prasmussen/gandi-api/client" + gandiClient "github.com/prasmussen/gandi-api/client" gandiDomain "github.com/prasmussen/gandi-api/domain" gandiZone "github.com/prasmussen/gandi-api/domain/zone" - gandiZoneVersion "github.com/prasmussen/gandi-api/domain/zone/version" gandiRecord "github.com/prasmussen/gandi-api/domain/zone/record" + gandiZoneVersion "github.com/prasmussen/gandi-api/domain/zone/version" + gandiOperation "github.com/prasmussen/gandi-api/operation" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" ) -type GandiHandler struct { +type GandiProvider struct { record *gandiRecord.Record zoneHandler *gandiZone.Zone zone *gandiZone.ZoneInfoBase zoneVersion *gandiZoneVersion.Version + operation *gandiOperation.Operation root string zoneDomain string zoneSuffix string @@ -26,77 +29,81 @@ type GandiHandler struct { } func init() { - gandiHandler := &GandiHandler{} - - apiKey := os.Getenv("GANDI_APIKEY") - if len(apiKey) == 0 { - logrus.Infof("GANDI_APIKEY is not set, skipping init of %s provider", gandiHandler.GetName()) - return - } + providers.RegisterProvider("gandi", &GandiProvider{}) +} - if err := RegisterProvider("gandi", gandiHandler); err != nil { - logrus.Fatal("Could not register Gandi provider") +func (g *GandiProvider) Init(rootDomainName string) error { + var apiKey string + if apiKey = os.Getenv("GANDI_APIKEY"); len(apiKey) == 0 { + return fmt.Errorf("GANDI_APIKEY is not set") } - systemType := gandi.Production + systemType := gandiClient.Production testing := os.Getenv("GANDI_TESTING") if len(testing) != 0 { logrus.Infof("GANDI_TESTING is set, using testing platform") - systemType = gandi.Testing + systemType = gandiClient.Testing } - client := gandi.New(apiKey, systemType) - gandiHandler.record = gandiRecord.New(client) - gandiHandler.zoneVersion = gandiZoneVersion.New(client) + client := gandiClient.New(apiKey, systemType) + g.record = gandiRecord.New(client) + g.zoneVersion = gandiZoneVersion.New(client) + g.operation = gandiOperation.New(client) - root := strings.TrimSuffix(dns.RootDomainName, ".") + root := utils.UnFqdn(rootDomainName) split_root := strings.Split(root, ".") - split_zoneDomain := split_root[len(split_root)-2:len(split_root)] + split_zoneDomain := split_root[len(split_root)-2 : len(split_root)] zoneDomain := strings.Join(split_zoneDomain, ".") domain := gandiDomain.New(client) domainInfo, err := domain.Info(zoneDomain) if err != nil { - logrus.Fatalf("Failed to get zone ID for domain %s: %v", zoneDomain, err) + return fmt.Errorf("Failed to get zone ID for domain %s: %v", zoneDomain, err) } zoneId := domainInfo.ZoneId zone := gandiZone.New(client) zones, err := zone.List() if err != nil { - logrus.Fatalf("Failed to list hosted zones: %v", err) + return fmt.Errorf("Failed to list hosted zones: %v", err) } found := false for _, z := range zones { if z.Id == zoneId { - gandiHandler.root = root - gandiHandler.zone = z + g.root = root + g.zone = z found = true break } } if !found { - logrus.Fatalf("Hosted zone %s is missing", root) + return fmt.Errorf("Zone for '%s' not found", root) } - gandiHandler.zoneDomain = zoneDomain - gandiHandler.zoneSuffix = fmt.Sprintf(".%s", zoneDomain) - gandiHandler.sub = strings.TrimSuffix(root, zoneDomain) - gandiHandler.zoneHandler = zone + g.zoneDomain = zoneDomain + g.zoneSuffix = fmt.Sprintf(".%s", zoneDomain) + g.sub = strings.TrimSuffix(root, zoneDomain) + g.zoneHandler = zone - logrus.Infof("Configured %s for domain %s using hosted zone %q ", gandiHandler.GetName(), root, gandiHandler.zone.Name) + logrus.Infof("Configured %s for domain '%s' using zone '%s'", g.GetName(), root, g.zone.Name) + return nil } -func (*GandiHandler) GetName() string { +func (*GandiProvider) GetName() string { return "Gandi" } -func (g *GandiHandler) AddRecord(record dns.DnsRecord) error { +func (g *GandiProvider) HealthCheck() error { + _, err := g.operation.Count() + return err +} + +func (g *GandiProvider) AddRecord(record utils.DnsRecord) error { newVersion, err := g.newZoneVersion() if err != nil { - return fmt.Errorf("Failed to add new record: %v", err) + return fmt.Errorf("Failed to add new record: %v", err) } if err := g.versionAddRecord(newVersion, record); err != nil { @@ -110,10 +117,10 @@ func (g *GandiHandler) AddRecord(record dns.DnsRecord) error { return nil } -func (g *GandiHandler) UpdateRecord(record dns.DnsRecord) error { +func (g *GandiProvider) UpdateRecord(record utils.DnsRecord) error { newVersion, err := g.newZoneVersion() if err != nil { - return fmt.Errorf("Failed to update record: %v", err) + return fmt.Errorf("Failed to update record: %v", err) } if err := g.versionRemoveRecord(newVersion, record); err != nil { @@ -131,10 +138,10 @@ func (g *GandiHandler) UpdateRecord(record dns.DnsRecord) error { return err } -func (g *GandiHandler) RemoveRecord(record dns.DnsRecord) error { +func (g *GandiProvider) RemoveRecord(record utils.DnsRecord) error { newVersion, err := g.newZoneVersion() if err != nil { - return fmt.Errorf("Failed to remove record: %v", err) + return fmt.Errorf("Failed to remove record: %v", err) } if err := g.versionRemoveRecord(newVersion, record); err != nil { @@ -148,7 +155,7 @@ func (g *GandiHandler) RemoveRecord(record dns.DnsRecord) error { return nil } -func (g *GandiHandler) findRecords(record dns.DnsRecord, version int64) ([]gandiRecord.RecordInfo, error) { +func (g *GandiProvider) findRecords(record utils.DnsRecord, version int64) ([]gandiRecord.RecordInfo, error) { var records []gandiRecord.RecordInfo resp, err := g.record.List(g.zone.Id, version) if err != nil { @@ -166,8 +173,8 @@ func (g *GandiHandler) findRecords(record dns.DnsRecord, version int64) ([]gandi return records, nil } -func (g *GandiHandler) GetRecords() ([]dns.DnsRecord, error) { - var records []dns.DnsRecord +func (g *GandiProvider) GetRecords() ([]utils.DnsRecord, error) { + var records []utils.DnsRecord recordResp, err := g.record.List(g.zone.Id, g.zone.Version) if err != nil { @@ -205,7 +212,7 @@ func (g *GandiHandler) GetRecords() ([]dns.DnsRecord, error) { for fqdn, recordSet := range recordMap { for recordType, recordSlice := range recordSet { ttl := recordTTLs[fqdn][recordType] - record := dns.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} + record := utils.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} records = append(records, record) } } @@ -213,14 +220,12 @@ func (g *GandiHandler) GetRecords() ([]dns.DnsRecord, error) { return records, nil } -func (g *GandiHandler) parseName(record dns.DnsRecord) string { +func (g *GandiProvider) parseName(record utils.DnsRecord) string { name := strings.TrimSuffix(record.Fqdn, g.zoneSuffix) - return name } -func (g *GandiHandler) versionAddRecord(version int64, record dns.DnsRecord) error { - +func (g *GandiProvider) versionAddRecord(version int64, record utils.DnsRecord) error { name := g.parseName(record) for _, rec := range record.Records { suffix := fmt.Sprintf(".%s.", g.zoneDomain) @@ -244,7 +249,7 @@ func (g *GandiHandler) versionAddRecord(version int64, record dns.DnsRecord) err return nil } -func (g *GandiHandler) versionRemoveRecord(version int64, record dns.DnsRecord) error { +func (g *GandiProvider) versionRemoveRecord(version int64, record utils.DnsRecord) error { records, err := g.findRecords(record, version) if err != nil { return err @@ -261,26 +266,26 @@ func (g *GandiHandler) versionRemoveRecord(version int64, record dns.DnsRecord) return nil } -func (g *GandiHandler) newZoneVersion() (int64, error) { +func (g *GandiProvider) newZoneVersion() (int64, error) { // Get latest zone version zoneInfo, err := g.zoneHandler.Info(g.zone.Id) if err != nil { - logrus.Fatalf("Failed to refresh zone information: %v", g.zone.Name, err) + return 0, fmt.Errorf("Failed to refresh zone information: %v", g.zone.Name, err) } newVersion, err := g.zoneVersion.New(g.zone.Id, zoneInfo.Version) if err != nil { - logrus.Fatalf("Failed to create new version of zone %s: %v", g.zone.Name, err) + return 0, fmt.Errorf("Failed to create new version of zone %s: %v", g.zone.Name, err) } return newVersion, nil } -func (g *GandiHandler) setZoneVersion(version int64) (error) { +func (g *GandiProvider) setZoneVersion(version int64) error { _, err := g.zoneVersion.Set(g.zone.Id, version) if err != nil { - logrus.Fatalf("Failed to set version of zone %s to %v: %v", g.zone.Name, version, err) + return fmt.Errorf("Failed to set version of zone %s to %v: %v", g.zone.Name, version, err) } - return err + return nil } diff --git a/providers/pointhq.go b/providers/pointhq/pointhq.go similarity index 60% rename from providers/pointhq.go rename to providers/pointhq/pointhq.go index 3adef79..13f20ff 100644 --- a/providers/pointhq.go +++ b/providers/pointhq/pointhq.go @@ -1,73 +1,76 @@ -package providers +package pointhq import ( "fmt" - "github.com/Sirupsen/logrus" - "github.com/cognetoapps/go-pointdns" - "github.com/rancher/external-dns/dns" "os" "strings" + + "github.com/Sirupsen/logrus" + "github.com/cognetoapps/go-pointdns" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" ) +type PointHQProvider struct { + client *pointdns.PointClient + root string + zone pointdns.Zone +} + func init() { - apiToken := os.Getenv("POINTHQ_TOKEN") - if len(apiToken) == 0 { - logrus.Info("POINTHQ_TOKEN is not set, skipping init of PointHQ provider") - return - } + providers.RegisterProvider("pointhq", &PointHQProvider{}) +} - email := os.Getenv("POINTHQ_EMAIL") - if len(email) == 0 { - logrus.Info("POINTHQ_EMAIL is not set, skipping init of PointHQ provider") - return +func (d *PointHQProvider) Init(rootDomainName string) error { + var email, apiToken string + if email = os.Getenv("POINTHQ_EMAIL"); len(email) == 0 { + return fmt.Errorf("POINTHQ_EMAIL is not set") } - pointhqHandler := &PointHQHandler{} - if err := RegisterProvider("pointhq", pointhqHandler); err != nil { - logrus.Fatal("Could not register pointhqHandler provider") + if apiToken = os.Getenv("POINTHQ_TOKEN"); len(apiToken) == 0 { + return fmt.Errorf("POINTHQ_TOKEN is not set") } - pointhqHandler.root = strings.TrimSuffix(dns.RootDomainName, ".") - pointhqHandler.client = pointdns.NewClient(email, apiToken) + d.root = utils.UnFqdn(rootDomainName) + d.client = pointdns.NewClient(email, apiToken) - zones, err := pointhqHandler.client.Zones() + zones, err := d.client.Zones() if err != nil { - logrus.Fatalf("Failed to list hosted zones: %v", err) + return fmt.Errorf("Failed to list hosted zones: %v", err) } found := false for _, zone := range zones { - if zone.Name == pointhqHandler.root { + if zone.Name == d.root { found = true - pointhqHandler.zone = zone + d.zone = zone break } } if !found { - logrus.Fatalf("Hosted zone %s is missing", pointhqHandler.root) + return fmt.Errorf("Zone for '%s' not found", d.root) } - logrus.Infof("Configured %s with hosted zone %q ", pointhqHandler.GetName(), pointhqHandler.root) + logrus.Infof("Configured %s with zone '%s'", d.GetName(), d.root) + return nil } -type PointHQHandler struct { - client *pointdns.PointClient - root string - zone pointdns.Zone +func (*PointHQProvider) GetName() string { + return "PointHQ" } -func (*PointHQHandler) GetName() string { - return "PointHQ" +func (d *PointHQProvider) HealthCheck() error { + _, err := d.client.Zones() + return err } -func (d *PointHQHandler) parseName(record dns.DnsRecord) string { +func (d *PointHQProvider) parseName(record utils.DnsRecord) string { name := strings.TrimSuffix(record.Fqdn, fmt.Sprintf(".%s.", d.root)) - return name } -func (d *PointHQHandler) AddRecord(record dns.DnsRecord) error { +func (d *PointHQProvider) AddRecord(record utils.DnsRecord) error { name := d.parseName(record) for _, rec := range record.Records { recordInput := pointdns.Record{ @@ -86,7 +89,7 @@ func (d *PointHQHandler) AddRecord(record dns.DnsRecord) error { return nil } -func (d *PointHQHandler) FindRecords(record dns.DnsRecord) ([]pointdns.Record, error) { +func (d *PointHQProvider) FindRecords(record utils.DnsRecord) ([]pointdns.Record, error) { var records []pointdns.Record resp, err := d.zone.Records() if err != nil { @@ -102,7 +105,7 @@ func (d *PointHQHandler) FindRecords(record dns.DnsRecord) ([]pointdns.Record, e return records, nil } -func (d *PointHQHandler) UpdateRecord(record dns.DnsRecord) error { +func (d *PointHQProvider) UpdateRecord(record utils.DnsRecord) error { err := d.RemoveRecord(record) if err != nil { return err @@ -111,7 +114,7 @@ func (d *PointHQHandler) UpdateRecord(record dns.DnsRecord) error { return d.AddRecord(record) } -func (d *PointHQHandler) RemoveRecord(record dns.DnsRecord) error { +func (d *PointHQProvider) RemoveRecord(record utils.DnsRecord) error { records, err := d.FindRecords(record) if err != nil { return err @@ -127,9 +130,8 @@ func (d *PointHQHandler) RemoveRecord(record dns.DnsRecord) error { return nil } -func (d *PointHQHandler) GetRecords() ([]dns.DnsRecord, error) { - var records []dns.DnsRecord - +func (d *PointHQProvider) GetRecords() ([]utils.DnsRecord, error) { + var records []utils.DnsRecord recordResp, err := d.zone.Records() if err != nil { return records, fmt.Errorf("PointHQ API call has failed: %v", err) @@ -141,7 +143,7 @@ func (d *PointHQHandler) GetRecords() ([]dns.DnsRecord, error) { for _, rec := range recordResp { var fqdn string if rec.Name == "" { - fqdn = dns.RootDomainName + fqdn = d.root + "." } else { fqdn = rec.Name } @@ -167,9 +169,10 @@ func (d *PointHQHandler) GetRecords() ([]dns.DnsRecord, error) { for fqdn, recordSet := range recordMap { for recordType, recordSlice := range recordSet { ttl := recordTTLs[fqdn][recordType] - record := dns.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} + record := utils.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl} records = append(records, record) } } + return records, nil } diff --git a/providers/powerdns.go b/providers/powerdns/powerdns.go similarity index 55% rename from providers/powerdns.go rename to providers/powerdns/powerdns.go index 1c00a83..4197ee2 100644 --- a/providers/powerdns.go +++ b/providers/powerdns/powerdns.go @@ -1,64 +1,61 @@ -package providers +package powerdns import ( "fmt" "os" - "strings" "github.com/Sirupsen/logrus" "github.com/jgreat/powerdns" - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" ) +type PdnsProvider struct { + client *powerdns.PowerDNS + root string +} + func init() { - pdnsAPIKey := os.Getenv("POWERDNS_API_KEY") - if len(pdnsAPIKey) == 0 { - logrus.Info("POWERDNS_API_KEY is not set, skipping init of PowerDNS provider") - return - } + providers.RegisterProvider("powerdns", &PdnsProvider{}) +} - pdnsURL := os.Getenv("POWERDNS_URL") - if len(pdnsURL) == 0 { - logrus.Info("POWERDNS_URL is not set, skipping init of PowerDNS provider") - return +func (d *PdnsProvider) Init(rootDomainName string) error { + var url, apiKey string + if url = os.Getenv("POWERDNS_URL"); len(url) == 0 { + return fmt.Errorf("POWERDNS_URL is not set") } - pdnsHandler := &PdnsHandler{} - if err := RegisterProvider("powerdns", pdnsHandler); err != nil { - logrus.Fatal("Could not register powerdns provider") + if apiKey = os.Getenv("POWERDNS_API_KEY"); len(apiKey) == 0 { + return fmt.Errorf("POWERDNS_API_KEY is not set") } - pdnsHandler.root = strings.TrimSuffix(dns.RootDomainName, ".") - pdnsHandler.client = powerdns.New(pdnsURL, "", pdnsHandler.root, pdnsAPIKey) + d.root = utils.UnFqdn(rootDomainName) + d.client = powerdns.New(url, "", d.root, apiKey) - _, err := pdnsHandler.client.GetRecords() + _, err := d.client.GetRecords() if err != nil { - logrus.Fatalf("Failed to list records for %s : %v", pdnsHandler.root, err) + return fmt.Errorf("Failed to list records for '%s': %v", d.root, err) } - logrus.Infof("Configured %s with hosted zone %q ", pdnsHandler.GetName(), pdnsHandler.root) -} - -// PdnsHandler ... -type PdnsHandler struct { - client *powerdns.PowerDNS - root string + logrus.Infof("Configured %s with zone '%s'", d.GetName(), d.root) + return nil } -// GetName ... -func (*PdnsHandler) GetName() string { +func (*PdnsProvider) GetName() string { return "PowerDNS" } -// dns.DnsRecord.Fqdn has a trailing. | powerdns.Record.Name doesn't -func (d *PdnsHandler) parseName(record dns.DnsRecord) string { - name := strings.TrimSuffix(record.Fqdn, ".") +func (d *PdnsProvider) HealthCheck() error { + _, err := d.client.GetRecords() + return err +} - return name +// utils.DnsRecord.Fqdn has a trailing. | powerdns.Record.Name doesn't +func (d *PdnsProvider) parseName(record utils.DnsRecord) string { + return utils.UnFqdn(record.Fqdn) } -// AddRecord ... -func (d *PdnsHandler) AddRecord(record dns.DnsRecord) error { +func (d *PdnsProvider) AddRecord(record utils.DnsRecord) error { logrus.Debugf("Called AddRecord with: %v\n", record) name := d.parseName(record) _, err := d.client.AddRecord(name, record.Type, record.TTL, record.Records) @@ -69,7 +66,7 @@ func (d *PdnsHandler) AddRecord(record dns.DnsRecord) error { return nil } -func (d *PdnsHandler) findRecords(record dns.DnsRecord) ([]powerdns.Record, error) { +func (d *PdnsProvider) findRecords(record utils.DnsRecord) ([]powerdns.Record, error) { var records []powerdns.Record resp, err := d.client.GetRecords() if err != nil { @@ -77,7 +74,7 @@ func (d *PdnsHandler) findRecords(record dns.DnsRecord) ([]powerdns.Record, erro } name := d.parseName(record) - // dns.DnsRecord.Fqdn has a trailing. | powerdns.Record.Name doesn't + // utils.DnsRecord.Fqdn has a trailing. | powerdns.Record.Name doesn't logrus.Debugf("Parsed Name is %s\n", name) for _, rec := range resp { if rec.Name == name && rec.Type == record.Type { @@ -88,8 +85,7 @@ func (d *PdnsHandler) findRecords(record dns.DnsRecord) ([]powerdns.Record, erro return records, nil } -// UpdateRecord ... -func (d *PdnsHandler) UpdateRecord(record dns.DnsRecord) error { +func (d *PdnsProvider) UpdateRecord(record utils.DnsRecord) error { logrus.Debugf("Called UpdateRecord with: %v\n", record) err := d.RemoveRecord(record) @@ -101,7 +97,7 @@ func (d *PdnsHandler) UpdateRecord(record dns.DnsRecord) error { } // RemoveRecord ... Might be able to do this with out the for loop -func (d *PdnsHandler) RemoveRecord(record dns.DnsRecord) error { +func (d *PdnsProvider) RemoveRecord(record utils.DnsRecord) error { logrus.Debugf("Called RemoveRecord with: %v\n", record) records, err := d.findRecords(record) @@ -120,10 +116,9 @@ func (d *PdnsHandler) RemoveRecord(record dns.DnsRecord) error { return nil } -// GetRecords ... -func (d *PdnsHandler) GetRecords() ([]dns.DnsRecord, error) { - logrus.Debugln("Called GetRecords") - var records []dns.DnsRecord +func (d *PdnsProvider) GetRecords() ([]utils.DnsRecord, error) { + logrus.Debug("Called GetRecords") + var records []utils.DnsRecord pdnsRecords, err := d.client.GetRecords() if err != nil { @@ -143,7 +138,7 @@ func (d *PdnsHandler) GetRecords() ([]dns.DnsRecord, error) { if re.Fqdn == name { found = true cont := append(re.Records, rec.Content) - records[i] = dns.DnsRecord{ + records[i] = utils.DnsRecord{ Fqdn: name, Records: cont, Type: rec.Type, @@ -152,7 +147,7 @@ func (d *PdnsHandler) GetRecords() ([]dns.DnsRecord, error) { } } if !found { - r := dns.DnsRecord{ + r := utils.DnsRecord{ Fqdn: name, Records: []string{rec.Content}, Type: rec.Type, diff --git a/providers/provider.go b/providers/provider.go index ce01458..d0e713e 100644 --- a/providers/provider.go +++ b/providers/provider.go @@ -3,45 +3,36 @@ package providers import ( "fmt" "github.com/Sirupsen/logrus" - "github.com/rancher/external-dns/dns" - "github.com/rancher/external-dns/metadata" + "github.com/rancher/external-dns/utils" ) type Provider interface { - AddRecord(record dns.DnsRecord) error - RemoveRecord(record dns.DnsRecord) error - UpdateRecord(record dns.DnsRecord) error - GetRecords() ([]dns.DnsRecord, error) + Init(rootDomainName string) error GetName() string + HealthCheck() error + AddRecord(record utils.DnsRecord) error + RemoveRecord(record utils.DnsRecord) error + UpdateRecord(record utils.DnsRecord) error + GetRecords() ([]utils.DnsRecord, error) } var ( - providers map[string]Provider + providers = make(map[string]Provider) ) -func init() { - // try to resolve rancher-metadata before going further - // the resolution indicates that the network has been set - _, err := metadata.NewMetadataClient() - if err != nil { - logrus.Fatalf("Failed to configure rancher-metadata client: %v", err) - } -} - -func GetProvider(name string) Provider { +func GetProvider(name, rootDomainName string) (Provider, error) { if provider, ok := providers[name]; ok { - return provider + if err := provider.Init(rootDomainName); err != nil { + return nil, err + } + return provider, nil } - return providers["route53"] + return nil, fmt.Errorf("No such provider '%s'", name) } -func RegisterProvider(name string, provider Provider) error { - if providers == nil { - providers = make(map[string]Provider) - } +func RegisterProvider(name string, provider Provider) { if _, exists := providers[name]; exists { - return fmt.Errorf("provider already registered") + logrus.Fatalf("Provider '%s' tried to register twice", name) } providers[name] = provider - return nil } diff --git a/providers/rfc2136.go b/providers/rfc2136.go deleted file mode 100755 index c00bccf..0000000 --- a/providers/rfc2136.go +++ /dev/null @@ -1,191 +0,0 @@ -package providers - -import ( - "github.com/Sirupsen/logrus" - rdns "github.com/rancher/external-dns/dns" - "os" - "fmt" - "github.com/miekg/dns" - "net" - "time" -) - -type RFC2136Handler struct { - server string - port string - keyname string - key string - rootdomain string -} - -func (b RFC2136Handler) sendMessage(msg *dns.Msg) error { - c := new(dns.Client) - c.TsigSecret = map[string]string{b.keyname: b.key} - c.SingleInflight = true - msg.SetTsig(b.keyname, dns.HmacMD5, 300, time.Now().Unix()) - nameserver := net.JoinHostPort(b.server, b.port) - - r, _, err := c.Exchange(msg, nameserver) - if err != nil { - return err - } - - if r != nil && r.Rcode != dns.RcodeSuccess { - return fmt.Errorf("Bad return code: %s", dns.RcodeToString[r.Rcode]) - } - - return nil -} - -func (b RFC2136Handler) remove(fqdn string, rtype string) error { - logrus.Debugf("removing dns entry %s", fqdn) - m := new(dns.Msg) - m.SetUpdate(b.rootdomain) - rr, rrerr := dns.NewRR(fmt.Sprintf("%s 0 %s 0.0.0.0", dns.Fqdn(fqdn), rtype)) - if rrerr != nil { - return rrerr - } - rrs := make([]dns.RR, 1) - rrs[0] = rr - m.RemoveRRset(rrs) - return b.sendMessage(m) -} - -func (b RFC2136Handler) list() ([]dns.RR, error) { - logrus.Debugf("listing dns entries for %s", b.rootdomain) - t := new(dns.Transfer) - t.TsigSecret = map[string]string{b.keyname: b.key} - - m := new(dns.Msg) - - nameserver := net.JoinHostPort(b.server, b.port) - m.SetAxfr(b.rootdomain) - m.SetTsig(b.keyname, dns.HmacMD5, 300, time.Now().Unix()) - - env, err := t.In(m, nameserver) - if err != nil { - return nil, err; - } - envelope := 0 - records := make([]dns.RR, 0) - - // TODO what is an enveloppe, can i receive several of them ? - for e := range env { - if e.Error != nil { - fmt.Printf(";; %s\n", e.Error.Error()) - return nil, e.Error - } - records = append(records, e.RR...) - envelope++ - } - return records, nil -} - -func init() { - server := os.Getenv("DNS_HOST") - port := os.Getenv("DNS_PORT") - keyname := os.Getenv("TSIG_KEYNAME") - key := os.Getenv("TSIG_KEY") - if len(server) == 0 || len(port) == 0 || len(keyname) == 0 || len(key) == 0 { - logrus.Info("RFC2136 environnement not set, skipping init of RFC2136 provider") - return - } - - rfc2136Handler := &RFC2136Handler{server, port, dns.Fqdn(keyname), key, dns.Fqdn(rdns.RootDomainName)} - if err := RegisterProvider("RFC2136", rfc2136Handler); err != nil { - logrus.Fatal("Could not register RFC2136 provider") - } - logrus.Infof("Configured %s with zone %s and server %s", rfc2136Handler.GetName(), rdns.RootDomainName, server) - -} - -/* Provider implementation */ -func (b *RFC2136Handler) AddRecord(record rdns.DnsRecord) error { - - logrus.Debugf("adding dns entry %s", record.Fqdn) - m := new(dns.Msg) - m.SetUpdate(b.rootdomain) - - rrs := make([]dns.RR, 0) - for _, rec := range record.Records { - logrus.Debugf("adding dns RR %s for %s", rec, record.Fqdn) - - rr, err := dns.NewRR(fmt.Sprintf("%s %d %s %s", record.Fqdn, record.TTL, record.Type, rec)) - if err != nil { - return err - } - rrs = append(rrs, rr) - } - m.Insert(rrs) - return b.sendMessage(m) -} - -func (b *RFC2136Handler) RemoveRecord(record rdns.DnsRecord) error { - err := b.remove(record.Fqdn, record.Type) - if err != nil { - return fmt.Errorf("RFC2136 API call has failed: %v", err) - } - return nil -} - -func (b *RFC2136Handler) UpdateRecord(record rdns.DnsRecord) error { - err := b.RemoveRecord(record) - if err != nil { - return err - } else { - return b.AddRecord(record) - } -} - -func (b *RFC2136Handler) GetRecords() ([]rdns.DnsRecord, error) { - list, err := b.list() - records := make([]rdns.DnsRecord, 0) - if err != nil { - return records, err - } - - OuterLoop: - for _, rr := range list { - if rr.Header().Class != dns.ClassINET { - continue - } - - rrFqdn := rr.Header().Name - rrTTL := int(rr.Header().Ttl) - var rrType, rrValue string - switch rr.Header().Rrtype { - case dns.TypeCNAME: - rrValue = rr.(*dns.CNAME).Target - rrType = "CNAME" - case dns.TypeA: - rrValue = rr.(*dns.A).A.String() - rrType = "A" - case dns.TypeAAAA: - rrValue = rr.(*dns.AAAA).AAAA.String() - rrType = "AAAA" - default: - continue // Unhandled record type - } - - for idx, existingRecord := range records { - if existingRecord.Fqdn == rrFqdn && existingRecord.Type == rrType { - records[idx].Records = append(records[idx].Records, rrValue) - continue OuterLoop - } - } - - record := rdns.DnsRecord{ - Fqdn: rrFqdn, - Type: rrType, - TTL: rrTTL, - Records: []string{rrValue}, - } - records = append(records, record) - } - - return records, nil -} - -func (b *RFC2136Handler) GetName() string { - return "RFC2136" -} diff --git a/providers/rfc2136/rfc2136.go b/providers/rfc2136/rfc2136.go new file mode 100644 index 0000000..3672a8f --- /dev/null +++ b/providers/rfc2136/rfc2136.go @@ -0,0 +1,207 @@ +package rfc2136 + +import ( + "fmt" + "net" + "os" + "time" + + "github.com/Sirupsen/logrus" + "github.com/miekg/dns" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" +) + +type RFC2136Provider struct { + nameserver string + zoneName string + tsigKeyName string + tsigSecret string +} + +func init() { + providers.RegisterProvider("rfc2136", &RFC2136Provider{}) +} + +func (r *RFC2136Provider) Init(rootDomainName string) error { + var host, port, keyName, secret string + if host = os.Getenv("RFC2136_HOST"); len(host) == 0 { + return fmt.Errorf("RFC2136_HOST is not set") + } + + if port = os.Getenv("RFC2136_PORT"); len(port) == 0 { + return fmt.Errorf("RFC2136_PORT is not set") + } + + if keyName = os.Getenv("RFC2136_TSIG_KEYNAME"); len(keyName) == 0 { + return fmt.Errorf("RFC2136_TSIG_KEYNAME is not set") + } + + if secret = os.Getenv("RFC2136_TSIG_SECRET"); len(secret) == 0 { + return fmt.Errorf("RFC2136_TSIG_SECRET is not set") + } + + r.nameserver = net.JoinHostPort(host, port) + r.zoneName = dns.Fqdn(rootDomainName) + r.tsigKeyName = dns.Fqdn(keyName) + r.tsigSecret = secret + + logrus.Infof("Configured %s with zone '%s' and nameserver '%s'", + r.GetName(), r.zoneName, r.nameserver) + + return nil +} + +func (*RFC2136Provider) GetName() string { + return "RFC2136" +} + +func (r *RFC2136Provider) HealthCheck() error { + _, err := r.GetRecords() + return err +} + +func (r *RFC2136Provider) AddRecord(record utils.DnsRecord) error { + logrus.Debugf("Adding RRset '%s %s'", record.Fqdn, record.Type) + m := new(dns.Msg) + m.SetUpdate(r.zoneName) + rrs := make([]dns.RR, 0) + for _, rec := range record.Records { + logrus.Debugf("Adding RR: '%s %d %s %s'", record.Fqdn, record.TTL, record.Type, rec) + rr, err := dns.NewRR(fmt.Sprintf("%s %d %s %s", record.Fqdn, record.TTL, record.Type, rec)) + if err != nil { + return fmt.Errorf("Failed to build RR: %v", err) + } + rrs = append(rrs, rr) + } + + m.Insert(rrs) + err := r.sendMessage(m) + if err != nil { + return fmt.Errorf("RFC2136 query failed: %v", err) + } + + return nil +} + +func (r *RFC2136Provider) RemoveRecord(record utils.DnsRecord) error { + logrus.Debugf("Removing RRset '%s %s'", record.Fqdn, record.Type) + m := new(dns.Msg) + m.SetUpdate(r.zoneName) + rr, err := dns.NewRR(fmt.Sprintf("%s 0 %s 0.0.0.0", record.Fqdn, record.Type)) + if err != nil { + return fmt.Errorf("Could not construct RR: %v", err) + } + + rrs := make([]dns.RR, 1) + rrs[0] = rr + m.RemoveRRset(rrs) + err = r.sendMessage(m) + if err != nil { + return fmt.Errorf("RFC2136 query failed: %v", err) + } + + return nil +} + +func (r *RFC2136Provider) UpdateRecord(record utils.DnsRecord) error { + err := r.RemoveRecord(record) + if err != nil { + return err + } + + return r.AddRecord(record) +} + +func (r *RFC2136Provider) GetRecords() ([]utils.DnsRecord, error) { + records := make([]utils.DnsRecord, 0) + list, err := r.list() + if err != nil { + return records, err + } + +OuterLoop: + for _, rr := range list { + if rr.Header().Class != dns.ClassINET { + continue + } + + rrFqdn := rr.Header().Name + rrTTL := int(rr.Header().Ttl) + var rrType, rrValue string + switch rr.Header().Rrtype { + case dns.TypeCNAME: + rrValue = rr.(*dns.CNAME).Target + rrType = "CNAME" + case dns.TypeA: + rrValue = rr.(*dns.A).A.String() + rrType = "A" + case dns.TypeAAAA: + rrValue = rr.(*dns.AAAA).AAAA.String() + rrType = "AAAA" + default: + continue // Unhandled record type + } + + for idx, existingRecord := range records { + if existingRecord.Fqdn == rrFqdn && existingRecord.Type == rrType { + records[idx].Records = append(records[idx].Records, rrValue) + continue OuterLoop + } + } + + record := utils.DnsRecord{ + Fqdn: rrFqdn, + Type: rrType, + TTL: rrTTL, + Records: []string{rrValue}, + } + + records = append(records, record) + } + + return records, nil +} + +func (r *RFC2136Provider) sendMessage(msg *dns.Msg) error { + c := new(dns.Client) + c.TsigSecret = map[string]string{r.tsigKeyName: r.tsigSecret} + c.SingleInflight = true + msg.SetTsig(r.tsigKeyName, dns.HmacMD5, 300, time.Now().Unix()) + resp, _, err := c.Exchange(msg, r.nameserver) + if err != nil { + return err + } + + if resp != nil && resp.Rcode != dns.RcodeSuccess { + return fmt.Errorf("Bad return code: %s", dns.RcodeToString[resp.Rcode]) + } + + return nil +} + +func (r *RFC2136Provider) list() ([]dns.RR, error) { + logrus.Debugf("Fetching records for '%s'", r.zoneName) + t := new(dns.Transfer) + t.TsigSecret = map[string]string{r.tsigKeyName: r.tsigSecret} + + m := new(dns.Msg) + m.SetAxfr(r.zoneName) + m.SetTsig(r.tsigKeyName, dns.HmacMD5, 300, time.Now().Unix()) + + env, err := t.In(m, r.nameserver) + if err != nil { + return nil, fmt.Errorf("Failed to fetch records via AXFR: %v", err) + } + + records := make([]dns.RR, 0) + for e := range env { + if e.Error != nil { + logrus.Errorf("AXFR envelope error: %v", e.Error) + continue + } + records = append(records, e.RR...) + } + + return records, nil +} diff --git a/providers/route53.go b/providers/route53/route53.go old mode 100755 new mode 100644 similarity index 53% rename from providers/route53.go rename to providers/route53/route53.go index 0a40ff1..3bb2acb --- a/providers/route53.go +++ b/providers/route53/route53.go @@ -1,4 +1,4 @@ -package providers +package route53 import ( "fmt" @@ -9,73 +9,72 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/route53" + awsRoute53 "github.com/aws/aws-sdk-go/service/route53" "github.com/juju/ratelimit" - "github.com/rancher/external-dns/dns" + "github.com/rancher/external-dns/providers" + "github.com/rancher/external-dns/utils" ) var route53MaxRetries int = 4 -type Route53Handler struct { - client *route53.Route53 +type Route53Provider struct { + client *awsRoute53.Route53 hostedZoneId string limiter *ratelimit.Bucket } func init() { + providers.RegisterProvider("route53", &Route53Provider{}) +} + +func (r *Route53Provider) Init(rootDomainName string) error { var region, accessKey, secretKey string if region = os.Getenv("AWS_REGION"); len(region) == 0 { - logrus.Info("AWS_REGION is not set, skipping init of Route 53 provider") - return + return fmt.Errorf("AWS_REGION is not set") } if accessKey = os.Getenv("AWS_ACCESS_KEY"); len(accessKey) == 0 { - logrus.Info("AWS_ACCESS_KEY is not set, skipping init of Route 53 provider") - return + return fmt.Errorf("AWS_ACCESS_KEY is not set") } if secretKey = os.Getenv("AWS_SECRET_KEY"); len(secretKey) == 0 { - logrus.Info("AWS_SECRET_KEY is not set, skipping init of Route 53 provider") - return - } - - handler := &Route53Handler{} - if err := RegisterProvider("route53", handler); err != nil { - logrus.Fatal("Could not register route53 provider") + return fmt.Errorf("AWS_SECRET_KEY is not set") } // Comply with the API's 5 req/s rate limit. If there are other - // clients using the same account the AWS SDK will throttle our - // requests once the API returns a "Rate exceeded" error. - handler.limiter = ratelimit.NewBucketWithRate(5.0, 1) + // clients using the same account the AWS SDK will throttle the + // requests automatically if the global rate limit is exhausted. + r.limiter = ratelimit.NewBucketWithRate(5.0, 1) creds := credentials.NewStaticCredentials(accessKey, secretKey, "") config := aws.NewConfig().WithMaxRetries(route53MaxRetries). WithCredentials(creds). WithRegion(region) - handler.client = route53.New(session.New(config)) + r.client = awsRoute53.New(session.New(config)) - if err := handler.setHostedZone(); err != nil { - logrus.Fatal(err) + if err := r.setHostedZone(rootDomainName); err != nil { + return err } logrus.Infof("Configured %s with hosted zone '%s' in region '%s' ", - handler.GetName(), dns.RootDomainName, region) + r.GetName(), rootDomainName, region) + + return nil } -func (r *Route53Handler) setHostedZone() error { +func (r *Route53Provider) setHostedZone(rootDomainName string) error { if envVal := os.Getenv("ROUTE53_ZONE_ID"); envVal != "" { r.hostedZoneId = strings.TrimSpace(envVal) - if err := r.validateHostedZoneId(); err != nil { + if err := r.validateHostedZoneId(rootDomainName); err != nil { return err } return nil } - + r.limiter.Wait(1) - params := &route53.ListHostedZonesByNameInput{ - DNSName: aws.String(strings.TrimSuffix(dns.RootDomainName, ".")), + params := &awsRoute53.ListHostedZonesByNameInput{ + DNSName: aws.String(utils.UnFqdn(rootDomainName)), MaxItems: aws.String("1"), } resp, err := r.client.ListHostedZonesByName(params) @@ -83,8 +82,8 @@ func (r *Route53Handler) setHostedZone() error { return fmt.Errorf("Could not list hosted zones: %v", err) } - if len(resp.HostedZones) == 0 || *resp.HostedZones[0].Name != dns.RootDomainName { - return fmt.Errorf("Hosted zone '%s' not found", dns.RootDomainName) + if len(resp.HostedZones) == 0 || *resp.HostedZones[0].Name != rootDomainName { + return fmt.Errorf("Hosted zone for '%s' not found", rootDomainName) } zoneId := *resp.HostedZones[0].Id @@ -96,9 +95,9 @@ func (r *Route53Handler) setHostedZone() error { return nil } -func (r *Route53Handler) validateHostedZoneId() error { +func (r *Route53Provider) validateHostedZoneId(rootDomainName string) error { r.limiter.Wait(1) - params := &route53.GetHostedZoneInput{ + params := &awsRoute53.GetHostedZoneInput{ Id: aws.String(r.hostedZoneId), } resp, err := r.client.GetHostedZone(params) @@ -107,47 +106,53 @@ func (r *Route53Handler) validateHostedZoneId() error { r.hostedZoneId, err) } - if *resp.HostedZone.Name != dns.RootDomainName { + if *resp.HostedZone.Name != rootDomainName { return fmt.Errorf("Hosted zone ID '%s' does not match name '%s'", - r.hostedZoneId, dns.RootDomainName) + r.hostedZoneId, rootDomainName) } return nil } -func (*Route53Handler) GetName() string { +func (*Route53Provider) GetName() string { return "Route 53" } -func (r *Route53Handler) AddRecord(record dns.DnsRecord) error { +func (r *Route53Provider) HealthCheck() error { + var params *awsRoute53.GetHostedZoneCountInput + _, err := r.client.GetHostedZoneCount(params) + return err +} + +func (r *Route53Provider) AddRecord(record utils.DnsRecord) error { return r.changeRecord(record, "UPSERT") } -func (r *Route53Handler) UpdateRecord(record dns.DnsRecord) error { +func (r *Route53Provider) UpdateRecord(record utils.DnsRecord) error { return r.changeRecord(record, "UPSERT") } -func (r *Route53Handler) RemoveRecord(record dns.DnsRecord) error { +func (r *Route53Provider) RemoveRecord(record utils.DnsRecord) error { return r.changeRecord(record, "DELETE") } -func (r *Route53Handler) changeRecord(record dns.DnsRecord, action string) error { +func (r *Route53Provider) changeRecord(record utils.DnsRecord, action string) error { r.limiter.Wait(1) - records := make([]*route53.ResourceRecord, len(record.Records)) + records := make([]*awsRoute53.ResourceRecord, len(record.Records)) for idx, value := range record.Records { - records[idx] = &route53.ResourceRecord{ + records[idx] = &awsRoute53.ResourceRecord{ Value: aws.String(value), } } - params := &route53.ChangeResourceRecordSetsInput{ + params := &awsRoute53.ChangeResourceRecordSetsInput{ HostedZoneId: aws.String(r.hostedZoneId), - ChangeBatch: &route53.ChangeBatch{ + ChangeBatch: &awsRoute53.ChangeBatch{ Comment: aws.String("Managed by Rancher"), - Changes: []*route53.Change{ + Changes: []*awsRoute53.Change{ { Action: aws.String(action), - ResourceRecordSet: &route53.ResourceRecordSet{ + ResourceRecordSet: &awsRoute53.ResourceRecordSet{ Name: aws.String(record.Fqdn), Type: aws.String(record.Type), TTL: aws.Int64(int64(record.TTL)), @@ -162,17 +167,17 @@ func (r *Route53Handler) changeRecord(record dns.DnsRecord, action string) error return err } -func (r *Route53Handler) GetRecords() ([]dns.DnsRecord, error) { +func (r *Route53Provider) GetRecords() ([]utils.DnsRecord, error) { r.limiter.Wait(1) - dnsRecords := []dns.DnsRecord{} - rrSets := []*route53.ResourceRecordSet{} - params := &route53.ListResourceRecordSetsInput{ + dnsRecords := []utils.DnsRecord{} + rrSets := []*awsRoute53.ResourceRecordSet{} + params := &awsRoute53.ListResourceRecordSetsInput{ HostedZoneId: aws.String(r.hostedZoneId), MaxItems: aws.String("100"), } err := r.client.ListResourceRecordSetsPages(params, - func(page *route53.ListResourceRecordSetsOutput, lastPage bool) bool { + func(page *awsRoute53.ListResourceRecordSetsOutput, lastPage bool) bool { rrSets = append(rrSets, page.ResourceRecordSets...) return !lastPage }) @@ -183,18 +188,19 @@ func (r *Route53Handler) GetRecords() ([]dns.DnsRecord, error) { for _, rrSet := range rrSets { // skip proprietary Route 53 alias resource record sets if rrSet.AliasTarget != nil { + logrus.Debug("Skipped Route53 alias RRset") continue } records := []string{} for _, rr := range rrSet.ResourceRecords { records = append(records, *rr.Value) } - - dnsRecord := dns.DnsRecord{ - Fqdn: *rrSet.Name, + + dnsRecord := utils.DnsRecord{ + Fqdn: *rrSet.Name, Records: records, - Type: *rrSet.Type, - TTL: int(*rrSet.TTL), + Type: *rrSet.Type, + TTL: int(*rrSet.TTL), } dnsRecords = append(dnsRecords, dnsRecord) } diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..1954a1a --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,47 @@ +package utils + +import ( + "strings" +) + +type DnsRecord struct { + Fqdn string + Records []string + Type string + TTL int +} + +type ServiceDnsRecord struct { + Fqdn string + ServiceName string + StackName string +} + +func ConvertToServiceDnsRecord(dnsRecord DnsRecord) ServiceDnsRecord { + splitted := strings.Split(dnsRecord.Fqdn, ".") + serviceRecord := ServiceDnsRecord{dnsRecord.Fqdn, splitted[0], splitted[1]} + return serviceRecord +} + +func ConvertToFqdn(serviceName, stackName, environmentName, rootDomainName string) string { + labels := []string{serviceName, stackName, environmentName, rootDomainName} + return strings.ToLower(strings.Join(labels, ".")) +} + +// Fqdn ensures that the name is a fqdn adding a trailing dot if necessary. +func Fqdn(name string) string { + n := len(name) + if n == 0 || name[n-1] == '.' { + return name + } + return name + "." +} + +// UnFqdn converts the fqdn into a name removing the trailing dot. +func UnFqdn(name string) string { + n := len(name) + if n != 0 && name[n-1] == '.' { + return name[:n-1] + } + return name +}