wrapper.go raw

   1  package internal
   2  
   3  import (
   4  	"fmt"
   5  	"strings"
   6  
   7  	"github.com/softlayer/softlayer-go/datatypes"
   8  	"github.com/softlayer/softlayer-go/services"
   9  	"github.com/softlayer/softlayer-go/session"
  10  	"github.com/softlayer/softlayer-go/sl"
  11  )
  12  
  13  type Wrapper struct {
  14  	session *session.Session
  15  }
  16  
  17  func NewWrapper(sess *session.Session) *Wrapper {
  18  	return &Wrapper{session: sess}
  19  }
  20  
  21  func (w Wrapper) AddTXTRecord(fqdn, domain, value string, ttl int) error {
  22  	service := services.GetDnsDomainService(w.session)
  23  
  24  	domainID, err := getDomainID(service, domain)
  25  	if err != nil {
  26  		return fmt.Errorf("failed to get domain ID: %w", err)
  27  	}
  28  
  29  	service.Options.Id = domainID
  30  
  31  	if _, err := service.CreateTxtRecord(sl.String(fqdn), sl.String(value), sl.Int(ttl)); err != nil {
  32  		return fmt.Errorf("failed to create TXT record: %w", err)
  33  	}
  34  
  35  	return nil
  36  }
  37  
  38  func (w Wrapper) CleanupTXTRecord(fqdn, domain string) error {
  39  	service := services.GetDnsDomainService(w.session)
  40  
  41  	domainID, err := getDomainID(service, domain)
  42  	if err != nil {
  43  		return fmt.Errorf("failed to get domain ID: %w", err)
  44  	}
  45  
  46  	service.Options.Id = domainID
  47  
  48  	records, err := findTxtRecords(service, fqdn)
  49  	if err != nil {
  50  		return fmt.Errorf("failed to find TXT records: %w", err)
  51  	}
  52  
  53  	return deleteResourceRecords(service, records)
  54  }
  55  
  56  func getDomainID(service services.Dns_Domain, domain string) (*int, error) {
  57  	res, err := service.GetByDomainName(sl.String(domain))
  58  	if err != nil {
  59  		return nil, err
  60  	}
  61  
  62  	for _, r := range res {
  63  		if r.Id == nil || toString(r.Name) != domain {
  64  			continue
  65  		}
  66  
  67  		return r.Id, nil
  68  	}
  69  
  70  	// The domain was not found by name.
  71  	// For subdomains this is not unusual in softlayer.
  72  	// So in case a subdomain like `sub.toplevel.tld` was used try again using the parent domain
  73  	// (strip the first part in the domain string -> `toplevel.tld`).
  74  	_, parent, found := strings.Cut(domain, ".")
  75  	if !found || !strings.Contains(parent, ".") {
  76  		return nil, fmt.Errorf("no data found for domain: %s", domain)
  77  	}
  78  
  79  	return getDomainID(service, parent)
  80  }
  81  
  82  func findTxtRecords(service services.Dns_Domain, fqdn string) ([]datatypes.Dns_Domain_ResourceRecord, error) {
  83  	var results []datatypes.Dns_Domain_ResourceRecord
  84  
  85  	records, err := service.GetResourceRecords()
  86  	if err != nil {
  87  		return nil, err
  88  	}
  89  
  90  	for _, record := range records {
  91  		if toString(record.Host) == fqdn && toString(record.Type) == "txt" {
  92  			results = append(results, record)
  93  		}
  94  	}
  95  
  96  	if len(results) == 0 {
  97  		return nil, fmt.Errorf("no data found of fqdn: %s", fqdn)
  98  	}
  99  
 100  	return results, nil
 101  }
 102  
 103  func deleteResourceRecords(service services.Dns_Domain, records []datatypes.Dns_Domain_ResourceRecord) error {
 104  	resourceRecord := services.GetDnsDomainResourceRecordService(service.Session)
 105  
 106  	// TODO maybe a bug: only the last record will be deleted
 107  	for _, record := range records {
 108  		resourceRecord.Options.Id = record.Id
 109  	}
 110  
 111  	_, err := resourceRecord.DeleteObject()
 112  	if err != nil {
 113  		return fmt.Errorf("no data found of fqdn: %w", err)
 114  	}
 115  
 116  	return nil
 117  }
 118  
 119  func toString(v *string) string {
 120  	if v == nil {
 121  		return ""
 122  	}
 123  
 124  	return *v
 125  }
 126