// Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { err := d.login() if err != nil { return err } fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) data := map[string]interface{}{ "rdata": map[string]string{ "txtdata": value, }, "ttl": strconv.Itoa(ttl), } resource := fmt.Sprintf("TXTRecord/%s/%s/", domain, fqdn) _, err = d.sendRequest("POST", resource, data) if err != nil { return err } err = d.publish(domain, "Added TXT record for ACME dns-01 challenge using lego client") if err != nil { return err } err = d.logout() if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(fqdn) if err != nil { return err } set, err := c.findTxtRecord(fqdn) if err != nil { return err } rrsets := rrSets{ RRSets: []rrSet{ rrSet{ Name: set.Name, Type: set.Type, ChangeType: "DELETE", }, }, } body, err := json.Marshal(rrsets) if err != nil { return err } _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters. func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := p.getHostedZoneInfo(fqdn) if err != nil { return err } // Get all TXT records for the specified domain. resources, err := p.linode.GetResourcesByType(zone.domainId, "TXT") if err != nil { return err } // Remove the specified resource, if it exists. for _, resource := range resources { if resource.Name == zone.resourceName && resource.Target == value { resp, err := p.linode.DeleteDomainResource(resource.DomainID, resource.ResourceID) if err != nil { return err } if resp.ResourceID != resource.ResourceID { return errors.New("Error deleting resource: resource IDs do not match!") } break } } return nil }
// CleanUp removes a given record that was generated by Present func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) provider.recordIDsMu.Lock() recordID, ok := provider.recordIDs[fqdn] provider.recordIDsMu.Unlock() if !ok { return fmt.Errorf("Unknown recordID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) zoneRecord, err := provider.getZoneInformationByName(authZone) if err != nil { return err } _, err = provider.client.RemoveRecord(zoneRecord.ID, recordID) if err != nil { return err } provider.recordIDsMu.Lock() delete(provider.recordIDs, fqdn) provider.recordIDsMu.Unlock() return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(domain) if err != nil { return err } rec := &dns.ResourceRecordSet{ Name: fqdn, Rrdatas: []string{value}, Ttl: int64(ttl), Type: "TXT", } change := &dns.Change{ Additions: []*dns.ResourceRecordSet{rec}, } chg, err := c.client.Changes.Create(c.project, zone, change).Do() if err != nil { return err } // wait for change to be acknowledged for chg.Status == "pending" { time.Sleep(time.Second) chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do() if err != nil { return err } } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZoneID(fqdn) if err != nil { return err } rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) rec := dns.RecordSet{ Name: &relative, Properties: &dns.RecordSetProperties{ TTL: to.Int64Ptr(60), TXTRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}}, }, } _, err = rsc.CreateOrUpdate(c.resourceGroup, zone, relative, dns.TXT, rec, "", "") if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified // parameters. It does this by restoring the old Gandi DNS zone and // removing the temporary one created by Present. func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // acquire lock and retrieve zoneID, newZoneID and authZone d.inProgressMu.Lock() defer d.inProgressMu.Unlock() if _, ok := d.inProgressFQDNs[fqdn]; !ok { // if there is no cleanup information then just return return nil } zoneID := d.inProgressFQDNs[fqdn].zoneID newZoneID := d.inProgressFQDNs[fqdn].newZoneID authZone := d.inProgressFQDNs[fqdn].authZone delete(d.inProgressFQDNs, fqdn) delete(d.inProgressAuthZones, authZone) // perform API actions to restore old gandi zone for authZone err := d.setZone(authZone, zoneID) if err != nil { return err } err = d.deleteZone(newZoneID) if err != nil { return err } return nil }
// CleanUp removes the record matching the specified parameters. func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } recordId, err := c.FindExistingRecordId(zone, recordName) if err != nil { return err } if recordId != 0 { record := egoscale.DNSRecord{ Id: recordId, } err = c.client.DeleteRecord(zone, record) if err != nil { return errors.New("Error while deleting DNS record: " + err.Error()) } } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain) if err != nil { return err } recordId, err := c.FindExistingRecordId(zone, recordName) if err != nil { return err } record := egoscale.DNSRecord{ Name: recordName, Ttl: ttl, Content: value, RecordType: "TXT", } if recordId == 0 { _, err := c.client.CreateRecord(zone, record) if err != nil { return errors.New("Error while creating DNS record: " + err.Error()) } } else { record.Id = recordId _, err := c.client.UpdateRecord(zone, record) if err != nil { return errors.New("Error while updating DNS record: " + err.Error()) } } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := c.getHostedZoneID(fqdn) if err != nil { return err } rec := RackspaceRecords{ RackspaceRecord: []RackspaceRecord{{ Name: acme.UnFqdn(fqdn), Type: "TXT", Data: value, TTL: 300, }}, } body, err := json.Marshal(rec) if err != nil { return err } _, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body)) if err != nil { return err } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := c.getHostedZoneID(fqdn) if err != nil { return err } rec := cloudFlareRecord{ Type: "TXT", Name: acme.UnFqdn(fqdn), Content: value, TTL: 120, } body, err := json.Marshal(rec) if err != nil { return err } _, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body)) if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown record ID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record/%d", authZone, recordID) err = d.client.Delete(reqURL, nil) if err != nil { fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err) return err } // Delete record ID from map d.recordIDsMu.Lock() delete(d.recordIDs, fqdn) d.recordIDsMu.Unlock() return nil }
// Present creates a TXT record using the specified parameters func (d *DNSProvider) Present(domain, token, keyAuth string) error { // txtRecordRequest represents the request body to DO's API to make a TXT record type txtRecordRequest struct { RecordType string `json:"type"` Name string `json:"name"` Data string `json:"data"` } // txtRecordResponse represents a response from DO's API after making a TXT record type txtRecordResponse struct { DomainRecord struct { ID int `json:"id"` Type string `json:"type"` Name string `json:"name"` Data string `json:"data"` } `json:"domain_record"` } fqdn, value, _ := acme.DNS01Record(domain, keyAuth) reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, domain) reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value} body, err := json.Marshal(reqData) if err != nil { return err } req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var errInfo digitalOceanAPIError json.NewDecoder(resp.Body).Decode(&errInfo) return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) } // Everything looks good; but we'll need the ID later to delete the record var respData txtRecordResponse err = json.NewDecoder(resp.Body).Decode(&respData) if err != nil { return err } d.recordIDsMu.Lock() d.recordIDs[fqdn] = respData.DomainRecord.ID d.recordIDsMu.Unlock() return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (d *DNSProvider) Present(domain, token, keyAuth string) error { // txtRecordRequest represents the request body to DO's API to make a TXT record type txtRecordRequest struct { FieldType string `json:"fieldType"` SubDomain string `json:"subDomain"` Target string `json:"target"` TTL int `json:"ttl"` } // txtRecordResponse represents a response from DO's API after making a TXT record type txtRecordResponse struct { ID int `json:"id"` FieldType string `json:"fieldType"` SubDomain string `json:"subDomain"` Target string `json:"target"` TTL int `json:"ttl"` Zone string `json:"zone"` } fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) // Parse domain name authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) subDomain := d.extractRecordName(fqdn, authZone) reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) reqData := txtRecordRequest{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: ttl} var respData txtRecordResponse // Create TXT record err = d.client.Post(reqURL, reqData, &respData) if err != nil { fmt.Printf("Error when call OVH api to add record : %q \n", err) return err } // Apply the change reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone) err = d.client.Post(reqURL, nil, nil) if err != nil { fmt.Printf("Error when call OVH api to refresh zone : %q \n", err) return err } d.recordIDsMu.Lock() d.recordIDs[fqdn] = respData.ID d.recordIDsMu.Unlock() return nil }
// CleanUp removes the TXT record matching the specified parameters. func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(domain) if err != nil { return err } name := acme.UnFqdn(fqdn) _, err = c.client.Records.Delete(zone.Zone, name, "TXT") return err }
// Present creates a TXT record using the specified parameters. func (p *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := p.getHostedZoneInfo(fqdn) if err != nil { return err } if _, err = p.linode.CreateDomainResourceTXT(zone.domainId, acme.UnFqdn(fqdn), value, 60); err != nil { return err } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(fqdn) if err != nil { return err } name := fqdn // pre-v1 API wants non-fqdn if c.apiVersion == 0 { name = acme.UnFqdn(fqdn) } rec := pdnsRecord{ Content: "\"" + value + "\"", Disabled: false, // pre-v1 API Type: "TXT", Name: name, TTL: 120, } rrsets := rrSets{ RRSets: []rrSet{ rrSet{ Name: name, ChangeType: "REPLACE", Type: "TXT", Kind: "Master", TTL: 120, Records: []pdnsRecord{rec}, }, }, } body, err := json.Marshal(rrsets) if err != nil { return err } _, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body)) if err != nil { fmt.Println("here") return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) record, err := c.findTxtRecord(fqdn) if err != nil { return err } _, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil) if err != nil { return err } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zoneID, zoneName, err := c.getHostedZone(domain) if err != nil { return err } recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl) _, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes) if err != nil { return fmt.Errorf("dnspod API call failed: %v", err) } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(domain) if err != nil { return err } record := c.newTxtRecord(zone, fqdn, value, ttl) _, err = c.client.Records.Create(record) if err != nil && err != rest.ErrRecordExists { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters. func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) records, err := c.findTxtRecords(domain, fqdn) if err != nil { return err } for _, rec := range records { _, err := c.client.Domains.DeleteRecord(rec.DomainId, rec.Id) if err != nil { return err } } return nil }
// CleanUp removes the TXT record matching the specified parameters. func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zoneDomain, records, err := c.findTxtRecords(domain, fqdn) if err != nil { return err } for _, rec := range records { err := c.client.DeleteDnsRecord(zoneDomain, rec.RecordID) if err != nil { return err } } return nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown record ID for '%s'", fqdn) } authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } authZone = acme.UnFqdn(authZone) reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID) req, err := http.NewRequest("DELETE", reqURL, nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) client := http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var errInfo digitalOceanAPIError json.NewDecoder(resp.Body).Decode(&errInfo) return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) } // Delete record ID from map d.recordIDsMu.Lock() delete(d.recordIDs, fqdn) d.recordIDsMu.Unlock() return nil }
// Present creates a TXT record to fulfil the DNS-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zoneDomain, err := c.getHostedZone(domain) if err != nil { return err } name := c.extractRecordName(fqdn, zoneDomain) err = c.client.CreateDnsRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl) if err != nil { return fmt.Errorf("Vultr API call failed: %v", err) } return nil }
// Present creates a TXT record to fulfil the dns-01 challenge. func (c *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, ttl := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZone(domain) if err != nil { return err } rec := &dns.ResourceRecordSet{ Name: fqdn, Rrdatas: []string{value}, Ttl: int64(ttl), Type: "TXT", } change := &dns.Change{ Additions: []*dns.ResourceRecordSet{rec}, } // Look for existing records. list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do() if err != nil { return err } if len(list.Rrsets) > 0 { // Attempt to delete the existing records when adding our new one. change.Deletions = list.Rrsets } chg, err := c.client.Changes.Create(c.project, zone, change).Do() if err != nil { return err } // wait for change to be acknowledged for chg.Status == "pending" { time.Sleep(time.Second) chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do() if err != nil { return err } } return nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers) if err != nil { return err } err = d.login() if err != nil { return err } resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn) url := fmt.Sprintf("%s/%s", dynBaseURL, resource) req, err := http.NewRequest("DELETE", url, nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Auth-Token", d.token) client := &http.Client{Timeout: time.Duration(10 * time.Second)} resp, err := client.Do(req) if err != nil { return err } resp.Body.Close() if resp.StatusCode != 200 { return fmt.Errorf("Dyn API request failed to delete TXT record HTTP status code %d", resp.StatusCode) } err = d.publish(authZone, "Removed TXT record for ACME dns-01 challenge using lego client") if err != nil { return err } err = d.logout() if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zone, err := c.getHostedZoneID(fqdn) if err != nil { return err } relative := toRelativeRecord(fqdn, acme.ToFqdn(zone)) rsc := dns.NewRecordSetsClient(c.subscriptionId) rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint) _, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "", "") if err != nil { return err } return nil }
// CleanUp removes the TXT record matching the specified parameters func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) zoneID, err := c.getHostedZoneID(fqdn) if err != nil { return err } record, err := c.findTxtRecord(fqdn, zoneID) if err != nil { return err } _, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil) if err != nil { return err } return nil }
// Present creates a record with a secret func (provider *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value, _ := acme.DNS01Record(domain, keyAuth) authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers) if err != nil { return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err) } // 1. Aurora will happily create the TXT record when it is provided a fqdn, // but it will only appear in the control panel and will not be // propagated to DNS servers. Extract and use subdomain instead. // 2. A trailing dot in the fqdn will cause Aurora to add a trailing dot to // the subdomain, resulting in _acme-challenge..<domain> rather // than _acme-challenge.<domain> subdomain := fqdn[0 : len(fqdn)-len(authZone)-1] authZone = acme.UnFqdn(authZone) zoneRecord, err := provider.getZoneInformationByName(authZone) reqData := records.CreateRecordRequest{ RecordType: "TXT", Name: subdomain, Content: value, TTL: 300, } respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData) if err != nil { return fmt.Errorf("Could not create record: '%s'.", err) } provider.recordIDsMu.Lock() provider.recordIDs[fqdn] = respData.ID provider.recordIDsMu.Unlock() return nil }
// CleanUp removes the TXT record matching the specified parameters func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { fqdn, _, _ := acme.DNS01Record(domain, keyAuth) // get the record's unique ID from when we created it d.recordIDsMu.Lock() recordID, ok := d.recordIDs[fqdn] d.recordIDsMu.Unlock() if !ok { return fmt.Errorf("unknown record ID for '%s'", fqdn) } reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, domain, recordID) req, err := http.NewRequest("DELETE", reqURL, nil) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken)) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode >= 400 { var errInfo digitalOceanAPIError json.NewDecoder(resp.Body).Decode(&errInfo) return fmt.Errorf("HTTP %d: %s: %s", resp.StatusCode, errInfo.ID, errInfo.Message) } // Delete record ID from map d.recordIDsMu.Lock() delete(d.recordIDs, fqdn) d.recordIDsMu.Unlock() return nil }