From 27a155f7ec388b2fc0e04c2a51abd3744f8fe05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sun, 18 May 2025 22:36:43 +0200 Subject: [PATCH 01/11] Initial commit --- integrationTest/profiles.json | 5 + providers/_all/all.go | 1 + providers/infomaniak/api.go | 143 +++++++++++++++++++++ providers/infomaniak/infomaniakProvider.go | 57 ++++++++ 4 files changed, 206 insertions(+) create mode 100644 providers/infomaniak/api.go create mode 100644 providers/infomaniak/infomaniakProvider.go diff --git a/integrationTest/profiles.json b/integrationTest/profiles.json index 860e62e84..2dd2613df 100644 --- a/integrationTest/profiles.json +++ b/integrationTest/profiles.json @@ -181,6 +181,11 @@ "TYPE": "HUAWEICLOUD", "domain": "$HUAWEICLOUD_DOMAIN" }, + "INFOMANIAK": { + "TYPE": "INFOMANIAK", + "domain": "$INFOMANIAK_DOMAIN", + "token": "$INFOMANIAK_TOKEN" + }, "INWX": { "TYPE": "INWX", "domain": "$INWX_DOMAIN", diff --git a/providers/_all/all.go b/providers/_all/all.go index c4f4820ff..0fa3c7106 100644 --- a/providers/_all/all.go +++ b/providers/_all/all.go @@ -31,6 +31,7 @@ import ( _ "github.com/StackExchange/dnscontrol/v4/providers/hexonet" _ "github.com/StackExchange/dnscontrol/v4/providers/hostingde" _ "github.com/StackExchange/dnscontrol/v4/providers/huaweicloud" + _ "github.com/StackExchange/dnscontrol/v4/providers/infomaniak" _ "github.com/StackExchange/dnscontrol/v4/providers/internetbs" _ "github.com/StackExchange/dnscontrol/v4/providers/inwx" _ "github.com/StackExchange/dnscontrol/v4/providers/linode" diff --git a/providers/infomaniak/api.go b/providers/infomaniak/api.go new file mode 100644 index 000000000..b8e46e18e --- /dev/null +++ b/providers/infomaniak/api.go @@ -0,0 +1,143 @@ +package infomaniak + +import ( + "encoding/json" + "fmt" + "net/http" +) + +const baseURL = "https://api.infomaniak.com/2" + +type dnssecRecord struct { + IsEnabled bool `json:"is_enabled"` +} + +type dnsZoneResponse struct { + Result string `json:"result"` + Data dnsZone `json:"data"` +} + +type dnsRecordResponse struct { + Result string `json:"result"` + Data []dnsRecord `json:"data"` +} + +type boolResponse struct { + Result string `json:"result"` + Data bool `json:"data"` +} +type dnsZone struct { + ID int64 `json:"id,omitempty"` + FQDN string `json:"fqdn,omitempty"` + DNSSEC dnssecRecord `json:"dnssec,omitempty"` + Nameservers []string `json:"nameservers,omitempty"` +} + +type dnsRecord struct { + ID string `json:"hostname,omitempty"` + Source string `json:"source,omitempty"` + Type string `json:"type,omitempty"` + TTL int64 `json:"ttl,omitempty"` + Target string `json:"target,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` +} + +// See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D +func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) { + reqURL := fmt.Sprintf("%s/zones/%s", baseURL, zone) + + req, err := http.NewRequest(http.MethodGet, reqURL, nil) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Bearer "+p.apiToken) + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + response := &dnsZoneResponse{} + + err = json.NewDecoder(res.Body).Decode(response) + if err != nil { + return nil, err + } + + return &response.Data, nil +} + +// See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D/records +func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { + reqURL := fmt.Sprintf("%s/zones/%s/records", baseURL, zone) + + req, err := http.NewRequest(http.MethodGet, reqURL, nil) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Bearer "+p.apiToken) + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + response := &dnsRecordResponse{} + + err = json.NewDecoder(res.Body).Decode(response) + if err != nil { + return nil, err + } + + return response.Data, nil +} + +func (p *infomaniakProvider) deleteDNSRecord(zone string, record string) error { + reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, record) + + req, err := http.NewRequest(http.MethodDelete, reqURL, nil) + if err != nil { + return err + } + req.Header.Add("Authorization", "Bearer "+p.apiToken) + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + return nil +} + +// func (p *infomaniakProvider) createDNSRecord(zoneID string, rec *dnsRecordCreate) (*dnsRecord, error) { +// reqURL := fmt.Sprintf("%s/dns_zones/%s/dns_records", baseURL, zoneID) + +// data, err := json.Marshal(rec) +// if err != nil { +// return nil, err +// } + +// req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(data)) +// if err != nil { +// return nil, err +// } +// req.Header.Add("Authorization", "Bearer "+n.apiToken) +// req.Header.Add("Content-Type", "application/json") + +// res, err := http.DefaultClient.Do(req) +// if err != nil { +// return nil, err +// } +// defer res.Body.Close() + +// record := &dnsRecord{} + +// err = json.NewDecoder(res.Body).Decode(record) +// if err != nil { +// return nil, err +// } + +// return record, nil +// } diff --git a/providers/infomaniak/infomaniakProvider.go b/providers/infomaniak/infomaniakProvider.go new file mode 100644 index 000000000..1b061a9af --- /dev/null +++ b/providers/infomaniak/infomaniakProvider.go @@ -0,0 +1,57 @@ +package infomaniak + +import ( + "encoding/json" + "errors" + + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/StackExchange/dnscontrol/v4/providers" +) + +// infomaniakProvider is the handle for operations. +type infomaniakProvider struct { + apiToken string // the account access token +} + +var features = providers.DocumentationNotes{ + // The default for unlisted capabilities is 'Cannot'. + // See providers/capabilities.go for the entire list of capabilities. + providers.CanGetZones: providers.Can(), +} + +func newInfomaniak(m map[string]string, message json.RawMessage) (providers.DNSServiceProvider, error) { + api := &infomaniakProvider{} + api.apiToken = m["token"] + if api.apiToken == "" { + return nil, errors.New("missing Infomaniak personal access token") + } + + return api, nil +} + +func init() { + const providerName = "INFOMANIAK" + const providerMaintainer = "@jbelien" + fns := providers.DspFuncs{ + Initializer: newInfomaniak, + } + providers.RegisterDomainServiceProviderType(providerName, fns, features) + providers.RegisterMaintainer(providerName, providerMaintainer) +} + +func (p *infomaniakProvider) GetNameservers(domain string) ([]*models.Nameserver, error) { + zone, err := p.getDNSZone(domain) + if err != nil { + return nil, err + } + + return models.ToNameservers(zone.Nameservers) +} + +func (p *infomaniakProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { + panic("GetZoneRecords not implemented") +} + +func (p *infomaniakProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { + panic("GetZoneRecordsCorrections not implemented") +} From 5c6325004db532e29d84193e29e57b8ed64f4840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sun, 18 May 2025 22:47:50 +0200 Subject: [PATCH 02/11] Add some documentation --- OWNERS | 1 + README.md | 1 + documentation/SUMMARY.md | 1 + documentation/provider/infomaniak.md | 38 ++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 documentation/provider/infomaniak.md diff --git a/OWNERS b/OWNERS index ff50183b7..02bfbb1f5 100644 --- a/OWNERS +++ b/OWNERS @@ -26,6 +26,7 @@ providers/hetzner @das7pad providers/hexonet @KaiSchwarz-cnic providers/hostingde @juliusrickert providers/huaweicloud @huihuimoe +providers/infomaniak @jbelien providers/internetbs @pragmaton providers/inwx @patschi providers/linode @koesie10 diff --git a/README.md b/README.md index d2410bf47..afd28641a 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Currently supported DNS providers: - hosting.de - Huawei Cloud DNS - Hurricane Electric DNS +- Infomaniak - INWX - Linode - Loopia diff --git a/documentation/SUMMARY.md b/documentation/SUMMARY.md index 2c39e1b80..a008f0cc6 100644 --- a/documentation/SUMMARY.md +++ b/documentation/SUMMARY.md @@ -131,6 +131,7 @@ * [hosting.de](provider/hostingde.md) * [Huawei Cloud DNS](provider/huaweicloud.md) * [Hurricane Electric DNS](provider/hedns.md) +* [Infomaniak](provider/infomaniak.md) * [Internet.bs](provider/internetbs.md) * [INWX](provider/inwx.md) * [Linode](provider/linode.md) diff --git a/documentation/provider/infomaniak.md b/documentation/provider/infomaniak.md new file mode 100644 index 000000000..076295230 --- /dev/null +++ b/documentation/provider/infomaniak.md @@ -0,0 +1,38 @@ +This is the provider for [Infomaniak](https://www.infomaniak.com/). + +## Configuration + +To use this provider, add an entry to `creds.json` with `TYPE` set to `INFOMANIAK` along with a Infomaniak account personal access token. + +Examples: + +{% code title="creds.json" %} +```json +{ + "netlify": { + "TYPE": "INFOMANIAK", + "token": "your-infomaniak-account-access-token", + } +} +``` +{% endcode %} + +## Metadata +This provider does not recognize any special metadata fields unique to Infomaniak. + +## Usage +An example configuration: + +{% code title="dnsconfig.js" %} +```javascript +var REG_INFOMANIAK = NewRegistrar("infomaniak"); +var DSP_INFOMANIAK = NewDnsProvider("infomaniak"); + +D("example.com", REG_INFOMANIAK, DnsProvider(DSP_INFOMANIAK), + A("test", "1.2.3.4"), +); +``` +{% endcode %} + +## Activation +DNSControl depends on a Infomaniak account personal access token. From 69edc60d8c770a97a75400795105f4ce2162a346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Mon, 19 May 2025 22:02:06 +0200 Subject: [PATCH 03/11] Update api.go --- providers/infomaniak/api.go | 75 ++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/providers/infomaniak/api.go b/providers/infomaniak/api.go index b8e46e18e..648e27f67 100644 --- a/providers/infomaniak/api.go +++ b/providers/infomaniak/api.go @@ -1,6 +1,7 @@ package infomaniak import ( + "bytes" "encoding/json" "fmt" "net/http" @@ -42,6 +43,14 @@ type dnsRecord struct { UpdatedAt int64 `json:"updated_at,omitempty"` } +type dnsRecordCreate struct { + Source string `json:"source,omitempty"` + Type string `json:"type,omitempty"` + TTL int64 `json:"ttl,omitempty"` + Target string `json:"target,omitempty"` +} + +// Get zone information // See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) { reqURL := fmt.Sprintf("%s/zones/%s", baseURL, zone) @@ -57,8 +66,9 @@ func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) { if err != nil { return nil, err } - response := &dnsZoneResponse{} + defer res.Body.Close() + response := &dnsZoneResponse{} err = json.NewDecoder(res.Body).Decode(response) if err != nil { return nil, err @@ -67,6 +77,7 @@ func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) { return &response.Data, nil } +// Retrieve all dns record for a given zone // See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D/records func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { reqURL := fmt.Sprintf("%s/zones/%s/records", baseURL, zone) @@ -82,8 +93,9 @@ func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { if err != nil { return nil, err } - response := &dnsRecordResponse{} + defer res.Body.Close() + response := &dnsRecordResponse{} err = json.NewDecoder(res.Body).Decode(response) if err != nil { return nil, err @@ -92,6 +104,8 @@ func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { return response.Data, nil } +// Delete a dns record +// See https://developer.infomaniak.com/docs/api/delete/2/zones/%7Bzone%7D/records/%7Brecord%7D func (p *infomaniakProvider) deleteDNSRecord(zone string, record string) error { reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, record) @@ -108,36 +122,43 @@ func (p *infomaniakProvider) deleteDNSRecord(zone string, record string) error { } defer res.Body.Close() + response := &boolResponse{} + err = json.NewDecoder(res.Body).Decode(response) + if err != nil { + return err + } + return nil } -// func (p *infomaniakProvider) createDNSRecord(zoneID string, rec *dnsRecordCreate) (*dnsRecord, error) { -// reqURL := fmt.Sprintf("%s/dns_zones/%s/dns_records", baseURL, zoneID) +// Create a dns record in a given zone +// See https://developer.infomaniak.com/docs/api/post/2/zones/%7Bzone%7D/records +func (p *infomaniakProvider) createDNSRecord(zone string, rec *dnsRecordCreate) (*dnsRecord, error) { + reqURL := fmt.Sprintf("%s/zones/%s/records", baseURL, zone) -// data, err := json.Marshal(rec) -// if err != nil { -// return nil, err -// } + data, err := json.Marshal(rec) + if err != nil { + return nil, err + } -// req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(data)) -// if err != nil { -// return nil, err -// } -// req.Header.Add("Authorization", "Bearer "+n.apiToken) -// req.Header.Add("Content-Type", "application/json") + req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(data)) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Bearer "+p.apiToken) + req.Header.Add("Content-Type", "application/json") -// res, err := http.DefaultClient.Do(req) -// if err != nil { -// return nil, err -// } -// defer res.Body.Close() + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() -// record := &dnsRecord{} + record := &dnsRecord{} + err = json.NewDecoder(res.Body).Decode(record) + if err != nil { + return nil, err + } -// err = json.NewDecoder(res.Body).Decode(record) -// if err != nil { -// return nil, err -// } - -// return record, nil -// } + return record, nil +} From 4f1b150937acdf37cebb3574731a23e7c72e5ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 14:41:29 +0200 Subject: [PATCH 04/11] Create auditrecords.go --- providers/infomaniak/auditrecords.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 providers/infomaniak/auditrecords.go diff --git a/providers/infomaniak/auditrecords.go b/providers/infomaniak/auditrecords.go new file mode 100644 index 000000000..549c82ded --- /dev/null +++ b/providers/infomaniak/auditrecords.go @@ -0,0 +1,15 @@ +package infomaniak + +import ( + "github.com/StackExchange/dnscontrol/v4/models" + "github.com/StackExchange/dnscontrol/v4/pkg/rejectif" +) + +// AuditRecords returns a list of errors corresponding to the records +// that aren't supported by this provider. If all records are +// supported, an empty list is returned. +func AuditRecords(records []*models.RecordConfig) []error { + a := rejectif.Auditor{} + + return a.Audit(records) +} From 540df588fe91d38fafd9460d0e64d4a716df0c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 14:45:05 +0200 Subject: [PATCH 05/11] Update api.go --- providers/infomaniak/api.go | 46 +++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/providers/infomaniak/api.go b/providers/infomaniak/api.go index 648e27f67..ea30fd825 100644 --- a/providers/infomaniak/api.go +++ b/providers/infomaniak/api.go @@ -13,19 +13,33 @@ type dnssecRecord struct { IsEnabled bool `json:"is_enabled"` } +type errorRecord struct { + Code string `json:"code"` + Description string `json:"description"` +} + type dnsZoneResponse struct { - Result string `json:"result"` - Data dnsZone `json:"data"` + Result string `json:"result"` + Data dnsZone `json:"data,omitempty"` + Error errorRecord `json:"error,omitempty"` +} + +type dnsRecordsResponse struct { + Result string `json:"result"` + Data []dnsRecord `json:"data,omitempty"` + Error errorRecord `json:"error,omitempty"` } type dnsRecordResponse struct { Result string `json:"result"` - Data []dnsRecord `json:"data"` + Data dnsRecord `json:"data,omitempty"` + Error errorRecord `json:"error,omitempty"` } type boolResponse struct { - Result string `json:"result"` - Data bool `json:"data"` + Result string `json:"result"` + Data bool `json:"data,omitempty"` + Error errorRecord `json:"error,omitempty"` } type dnsZone struct { ID int64 `json:"id,omitempty"` @@ -35,7 +49,7 @@ type dnsZone struct { } type dnsRecord struct { - ID string `json:"hostname,omitempty"` + ID int64 `json:"id,omitempty"` Source string `json:"source,omitempty"` Type string `json:"type,omitempty"` TTL int64 `json:"ttl,omitempty"` @@ -95,7 +109,7 @@ func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { } defer res.Body.Close() - response := &dnsRecordResponse{} + response := &dnsRecordsResponse{} err = json.NewDecoder(res.Body).Decode(response) if err != nil { return nil, err @@ -106,8 +120,8 @@ func (p *infomaniakProvider) getDNSRecords(zone string) ([]dnsRecord, error) { // Delete a dns record // See https://developer.infomaniak.com/docs/api/delete/2/zones/%7Bzone%7D/records/%7Brecord%7D -func (p *infomaniakProvider) deleteDNSRecord(zone string, record string) error { - reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, record) +func (p *infomaniakProvider) deleteDNSRecord(zone string, recordID string) error { + reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, recordID) req, err := http.NewRequest(http.MethodDelete, reqURL, nil) if err != nil { @@ -128,6 +142,10 @@ func (p *infomaniakProvider) deleteDNSRecord(zone string, record string) error { return err } + if response.Result == "error" { + return fmt.Errorf("failed to delete record %s in zone %s: %s", recordID, zone, response.Error.Description) + } + return nil } @@ -154,11 +172,15 @@ func (p *infomaniakProvider) createDNSRecord(zone string, rec *dnsRecordCreate) } defer res.Body.Close() - record := &dnsRecord{} - err = json.NewDecoder(res.Body).Decode(record) + response := &dnsRecordResponse{} + err = json.NewDecoder(res.Body).Decode(response) if err != nil { return nil, err } - return record, nil + if response.Result == "error" { + return nil, fmt.Errorf("failed to create %s record in zone %s: %s", rec.Type, zone, response.Error.Description) + } + + return &response.Data, nil } From a616f2d283c25dcbacc6c73fde6d59a3a788a68b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 14:45:20 +0200 Subject: [PATCH 06/11] [WIP] Update infomaniakProvider.go --- providers/infomaniak/infomaniakProvider.go | 76 ++++++++++++++++++++-- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/providers/infomaniak/infomaniakProvider.go b/providers/infomaniak/infomaniakProvider.go index 1b061a9af..af7041d09 100644 --- a/providers/infomaniak/infomaniakProvider.go +++ b/providers/infomaniak/infomaniakProvider.go @@ -3,8 +3,10 @@ package infomaniak import ( "encoding/json" "errors" + "fmt" "github.com/StackExchange/dnscontrol/v4/models" + "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" ) @@ -16,7 +18,14 @@ type infomaniakProvider struct { var features = providers.DocumentationNotes{ // The default for unlisted capabilities is 'Cannot'. // See providers/capabilities.go for the entire list of capabilities. - providers.CanGetZones: providers.Can(), + providers.CanGetZones: providers.Can(), + providers.CanUseCAA: providers.Can(), + providers.CanUseDNAME: providers.Can(), + providers.CanUseDS: providers.Can(), + providers.CanUseSSHFP: providers.Can(), + providers.CanUseTLSA: providers.Can(), + providers.CanUseSRV: providers.Can(), + providers.DocCreateDomains: providers.Can(), } func newInfomaniak(m map[string]string, message json.RawMessage) (providers.DNSServiceProvider, error) { @@ -33,7 +42,8 @@ func init() { const providerName = "INFOMANIAK" const providerMaintainer = "@jbelien" fns := providers.DspFuncs{ - Initializer: newInfomaniak, + Initializer: newInfomaniak, + RecordAuditor: AuditRecords, } providers.RegisterDomainServiceProviderType(providerName, fns, features) providers.RegisterMaintainer(providerName, providerMaintainer) @@ -49,9 +59,67 @@ func (p *infomaniakProvider) GetNameservers(domain string) ([]*models.Nameserver } func (p *infomaniakProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { - panic("GetZoneRecords not implemented") + records, err := p.getDNSRecords(domain) + if err != nil { + return nil, err + } + + cleanRecords := make(models.Records, 0) + + for _, r := range records { + recConfig := &models.RecordConfig{ + Original: r, + TTL: uint32(r.TTL), + Type: r.Type, + } + recConfig.SetLabelFromFQDN(r.Source, domain) + recConfig.SetTarget(r.Target) + + cleanRecords = append(cleanRecords, recConfig) + } + + return cleanRecords, nil } func (p *infomaniakProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { - panic("GetZoneRecordsCorrections not implemented") + var corrections []*models.Correction + + changes, actualChangeCount, err := diff2.ByRecord(existingRecords, dc, nil) + if err != nil { + return nil, 0, err + } + + for _, change := range changes { + switch change.Type { + case diff2.REPORT: + corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined}) + case diff2.CHANGE: + fmt.Printf("CHANGE: %+v\n", change.New) + // corrections = append(corrections, &models.Correction{ + // Msg: change.Msgs[0], + // F: func() error { + // return p.updateRecord(change.Old[0].Original.(dnsRecord), change.New[0], dc.Name) + // }, + // }) + case diff2.CREATE: + fmt.Printf("CREATE: %+v\n", change.New) + // corrections = append(corrections, &models.Correction{ + // Msg: change.Msgs[0], + // F: func() error { + // _, err := p.createDNSRecord(dc.Name, change.New[0]) + // return err + // }, + // }) + case diff2.DELETE: + rec := change.Old[0].Original.(dnsRecord) + corrections = append(corrections, &models.Correction{ + Msg: change.Msgs[0], + F: func() error { + return p.deleteDNSRecord(dc.Name, fmt.Sprintf("%v", rec.ID)) + }, + }) + } + } + + return corrections, actualChangeCount, nil } From b91dcc42f8c2452313d958b6ce7f1676f96eedf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 14:52:19 +0200 Subject: [PATCH 07/11] Update infomaniakProvider.go --- providers/infomaniak/infomaniakProvider.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/providers/infomaniak/infomaniakProvider.go b/providers/infomaniak/infomaniakProvider.go index af7041d09..8ad1667df 100644 --- a/providers/infomaniak/infomaniakProvider.go +++ b/providers/infomaniak/infomaniakProvider.go @@ -18,14 +18,14 @@ type infomaniakProvider struct { var features = providers.DocumentationNotes{ // The default for unlisted capabilities is 'Cannot'. // See providers/capabilities.go for the entire list of capabilities. - providers.CanGetZones: providers.Can(), - providers.CanUseCAA: providers.Can(), - providers.CanUseDNAME: providers.Can(), - providers.CanUseDS: providers.Can(), - providers.CanUseSSHFP: providers.Can(), - providers.CanUseTLSA: providers.Can(), - providers.CanUseSRV: providers.Can(), - providers.DocCreateDomains: providers.Can(), + providers.CanGetZones: providers.Can(), + providers.CanUseCAA: providers.Can(), + providers.CanUseDNAME: providers.Can(), + providers.CanUseDS: providers.Can(), + providers.CanUseSSHFP: providers.Can(), + providers.CanUseTLSA: providers.Can(), + providers.CanUseSRV: providers.Can(), + // providers.DocCreateDomains: providers.Can(), } func newInfomaniak(m map[string]string, message json.RawMessage) (providers.DNSServiceProvider, error) { From 83690755c5b3d1e5ecb4ef69e5a29ea236f12749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 15:03:11 +0200 Subject: [PATCH 08/11] Update infomaniak.md --- documentation/provider/infomaniak.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/provider/infomaniak.md b/documentation/provider/infomaniak.md index 076295230..845ea46c4 100644 --- a/documentation/provider/infomaniak.md +++ b/documentation/provider/infomaniak.md @@ -25,10 +25,10 @@ An example configuration: {% code title="dnsconfig.js" %} ```javascript -var REG_INFOMANIAK = NewRegistrar("infomaniak"); +var REG_NONE = NewRegistrar("none"); var DSP_INFOMANIAK = NewDnsProvider("infomaniak"); -D("example.com", REG_INFOMANIAK, DnsProvider(DSP_INFOMANIAK), +D("example.com", REG_NONE, DnsProvider(DSP_INFOMANIAK), A("test", "1.2.3.4"), ); ``` From 39d84ff21653dbf363ea46c943d0d6db1b013b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 15:06:09 +0200 Subject: [PATCH 09/11] Update infomaniak.md --- documentation/provider/infomaniak.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/provider/infomaniak.md b/documentation/provider/infomaniak.md index 845ea46c4..e2fe0febb 100644 --- a/documentation/provider/infomaniak.md +++ b/documentation/provider/infomaniak.md @@ -9,7 +9,7 @@ Examples: {% code title="creds.json" %} ```json { - "netlify": { + "infomaniak": { "TYPE": "INFOMANIAK", "token": "your-infomaniak-account-access-token", } From 5bb8c6eadf46d71e964e6268e847fb8619ba5480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Beli=C3=ABn?= Date: Sat, 24 May 2025 15:22:41 +0200 Subject: [PATCH 10/11] Update providers matrix --- documentation/provider/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/provider/index.md b/documentation/provider/index.md index f75f5a6bb..90fcc8ccc 100644 --- a/documentation/provider/index.md +++ b/documentation/provider/index.md @@ -42,6 +42,7 @@ If a feature is definitively not supported for whatever reason, we would also li | [`HEXONET`](hexonet.md) | ❌ | ✅ | ✅ | ❔ | ❌ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ❔ | ✅ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ❔ | | [`HOSTINGDE`](hostingde.md) | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ❔ | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ❌ | ✅ | ❌ | ❔ | ❌ | ✅ | ❔ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | +| [`INFOMANIAK`](infomaniak.md) | ❌ | ✅ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ❔ | ❔ | ❌ | ✅ | | [`INTERNETBS`](internetbs.md) | ❌ | ❌ | ✅ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❔ | | [`INWX`](inwx.md) | ❌ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ✅ | ✅ | ❔ | ✅ | ✅ | ✅ | ✅ | ❔ | ❔ | ❔ | ❔ | ✅ | ✅ | ✅ | | [`LINODE`](linode.md) | ❌ | ✅ | ❌ | ❔ | ❔ | ✅ | ❔ | ❔ | ❌ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❔ | ❌ | ❌ | ✅ | From 0cc0b5a34595e9cf9e01a20d9ae41fb2ed05a143 Mon Sep 17 00:00:00 2001 From: signalwerk Date: Fri, 28 Nov 2025 12:17:01 +0100 Subject: [PATCH 11/11] MOD: adjust handling for Infomaniak API format --- documentation/provider/index.md | 7 + providers/infomaniak/api.go | 41 ++++ providers/infomaniak/infomaniakProvider.go | 271 +++++++++++++++++++-- 3 files changed, 293 insertions(+), 26 deletions(-) diff --git a/documentation/provider/index.md b/documentation/provider/index.md index 50c15679f..fb243b3fb 100644 --- a/documentation/provider/index.md +++ b/documentation/provider/index.md @@ -56,6 +56,7 @@ Jump to a table: | [`HEXONET`](hexonet.md) | ❌ | ✅ | ✅ | | [`HOSTINGDE`](hostingde.md) | ❌ | ✅ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ❌ | ✅ | ❌ | +| [`INFOMANIAK`](infomaniak.md) | ❌ | ✅ | ❌ | | [`INTERNETBS`](internetbs.md) | ❌ | ❌ | ✅ | | [`INWX`](inwx.md) | ❌ | ✅ | ✅ | | [`JOKER`](joker.md) | ❌ | ✅ | ❌ | @@ -118,6 +119,7 @@ Jump to a table: | [`HEXONET`](hexonet.md) | ❔ | ✅ | ✅ | ❔ | | [`HOSTINGDE`](hostingde.md) | ❔ | ✅ | ✅ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ❔ | ✅ | ✅ | ✅ | +| [`INFOMANIAK`](infomaniak.md) | ❔ | ❔ | ❌ | ✅ | | [`INTERNETBS`](internetbs.md) | ❔ | ❔ | ❌ | ❔ | | [`INWX`](inwx.md) | ✅ | ✅ | ✅ | ✅ | | [`JOKER`](joker.md) | ❌ | ❌ | ✅ | ✅ | @@ -177,6 +179,7 @@ Jump to a table: | [`HEXONET`](hexonet.md) | ❌ | ❔ | ❔ | ✅ | ❔ | | [`HOSTINGDE`](hostingde.md) | ✅ | ❔ | ❌ | ✅ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ❌ | ❔ | ❌ | ❌ | ❌ | +| [`INFOMANIAK`](infomaniak.md) | ❌ | ✅ | ❌ | ❌ | ❌ | | [`INWX`](inwx.md) | ✅ | ❔ | ❔ | ✅ | ❔ | | [`JOKER`](joker.md) | ❌ | ❔ | ❌ | ❌ | ❌ | | [`LINODE`](linode.md) | ❔ | ❔ | ❌ | ❔ | ❔ | @@ -233,6 +236,7 @@ Jump to a table: | [`HEXONET`](hexonet.md) | ❔ | ❔ | ✅ | ❔ | | [`HOSTINGDE`](hostingde.md) | ❔ | ❌ | ✅ | ❔ | | [`HUAWEICLOUD`](huaweicloud.md) | ❔ | ❌ | ✅ | ❌ | +| [`INFOMANIAK`](infomaniak.md) | ❌ | ❌ | ✅ | ❌ | | [`INWX`](inwx.md) | ❔ | ✅ | ✅ | ✅ | | [`JOKER`](joker.md) | ❔ | ✅ | ✅ | ❌ | | [`LOOPIA`](loopia.md) | ❌ | ✅ | ✅ | ❌ | @@ -288,6 +292,7 @@ Jump to a table: | [`HEXONET`](hexonet.md) | ✅ | ❔ | ❔ | ❔ | ✅ | | [`HOSTINGDE`](hostingde.md) | ✅ | ❔ | ❔ | ✅ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ✅ | ❌ | ❔ | ❌ | ❌ | +| [`INFOMANIAK`](infomaniak.md) | ✅ | ❌ | ❌ | ✅ | ✅ | | [`INWX`](inwx.md) | ✅ | ✅ | ❔ | ✅ | ✅ | | [`JOKER`](joker.md) | ✅ | ❌ | ❔ | ❌ | ❌ | | [`LINODE`](linode.md) | ✅ | ❔ | ❔ | ❔ | ❔ | @@ -333,6 +338,7 @@ Jump to a table: | [`HETZNER_V2`](hetzner_v2.md) | ❌ | ❔ | ✅ | | [`HOSTINGDE`](hostingde.md) | ✅ | ❔ | ✅ | | [`HUAWEICLOUD`](huaweicloud.md) | ❔ | ❔ | ❌ | +| [`INFOMANIAK`](infomaniak.md) | ❌ | ❌ | ✅ | | [`INWX`](inwx.md) | ✅ | ❔ | ❔ | | [`JOKER`](joker.md) | ❔ | ❌ | ❌ | | [`LOOPIA`](loopia.md) | ❌ | ❌ | ❌ | @@ -414,6 +420,7 @@ Providers in this category and their maintainers are: |[`HEXONET`](hexonet.md)|@KaiSchwarz-cnic| |[`HOSTINGDE`](hostingde.md)|@membero| |[`HUAWEICLOUD`](huaweicloud.md)|@huihuimoe| +|[`INFOMANIAK`](infomaniak.md)|@jbelien| |[`INTERNETBS`](internetbs.md)|@pragmaton| |[`INWX`](inwx.md)|@patschi| |[`LINODE`](linode.md)|@koesie10| diff --git a/providers/infomaniak/api.go b/providers/infomaniak/api.go index ea30fd825..e092ac9e5 100644 --- a/providers/infomaniak/api.go +++ b/providers/infomaniak/api.go @@ -64,6 +64,11 @@ type dnsRecordCreate struct { Target string `json:"target,omitempty"` } +type dnsRecordUpdate struct { + Target string `json:"target,omitempty"` + TTL int64 `json:"ttl,omitempty"` +} + // Get zone information // See https://developer.infomaniak.com/docs/api/get/2/zones/%7Bzone%7D func (p *infomaniakProvider) getDNSZone(zone string) (*dnsZone, error) { @@ -184,3 +189,39 @@ func (p *infomaniakProvider) createDNSRecord(zone string, rec *dnsRecordCreate) return &response.Data, nil } + +// Update a dns record in a given zone +// See https://developer.infomaniak.com/docs/api/put/2/zones/%7Bzone%7D/records/%7Brecord%7D +func (p *infomaniakProvider) updateDNSRecord(zone string, recordID string, rec *dnsRecordUpdate) (*dnsRecord, error) { + reqURL := fmt.Sprintf("%s/zones/%s/records/%s", baseURL, zone, recordID) + + data, err := json.Marshal(rec) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, reqURL, bytes.NewReader(data)) + if err != nil { + return nil, err + } + req.Header.Add("Authorization", "Bearer "+p.apiToken) + req.Header.Add("Content-Type", "application/json") + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + + response := &dnsRecordResponse{} + err = json.NewDecoder(res.Body).Decode(response) + if err != nil { + return nil, err + } + + if response.Result == "error" { + return nil, fmt.Errorf("failed to update record %s in zone %s: %s", recordID, zone, response.Error.Description) + } + + return &response.Data, nil +} diff --git a/providers/infomaniak/infomaniakProvider.go b/providers/infomaniak/infomaniakProvider.go index 8ad1667df..62e04a57a 100644 --- a/providers/infomaniak/infomaniakProvider.go +++ b/providers/infomaniak/infomaniakProvider.go @@ -4,10 +4,12 @@ import ( "encoding/json" "errors" "fmt" + "strings" "github.com/StackExchange/dnscontrol/v4/models" "github.com/StackExchange/dnscontrol/v4/pkg/diff2" "github.com/StackExchange/dnscontrol/v4/providers" + "github.com/miekg/dns/dnsutil" ) // infomaniakProvider is the handle for operations. @@ -18,7 +20,6 @@ type infomaniakProvider struct { var features = providers.DocumentationNotes{ // The default for unlisted capabilities is 'Cannot'. // See providers/capabilities.go for the entire list of capabilities. - providers.CanGetZones: providers.Can(), providers.CanUseCAA: providers.Can(), providers.CanUseDNAME: providers.Can(), providers.CanUseDS: providers.Can(), @@ -58,23 +59,236 @@ func (p *infomaniakProvider) GetNameservers(domain string) ([]*models.Nameserver return models.ToNameservers(zone.Nameservers) } +// addTrailingDot adds a trailing dot if it's missing. +func addTrailingDot(target string) string { + if target == "" || target == "." { + return target + } + if !strings.HasSuffix(target, ".") { + return target + "." + } + return target +} + +// toRecordConfig converts a DNS record from Infomaniak API to RecordConfig. +func toRecordConfig(domain string, r dnsRecord) (*models.RecordConfig, error) { + rc := &models.RecordConfig{ + TTL: uint32(r.TTL), + Original: r, + } + + // Handle the source/label - Infomaniak uses empty string or "." for apex + label := r.Source + if label == "" || label == "." { + label = "@" + } + rc.SetLabel(label, domain) + + // Parse the target based on record type + rtype := r.Type + target := r.Target + + var err error + switch rtype { + case "A", "AAAA": + rc.Type = rtype + err = rc.SetTarget(target) + + case "CNAME", "NS", "DNAME": + rc.Type = rtype + // Add trailing dot and use AddOrigin to properly qualify the target + err = rc.SetTarget(dnsutil.AddOrigin(addTrailingDot(target), domain)) + + case "MX": + // Infomaniak returns MX as "priority target" (e.g., "5 mta-gw.infomaniak.ch") + rc.Type = rtype + err = rc.SetTargetMXString(addTrailingDot(target)) + + case "TXT": + rc.Type = rtype + // Infomaniak API returns TXT values wrapped in quotes, strip them + if len(target) >= 2 && strings.HasPrefix(target, "\"") && strings.HasSuffix(target, "\"") { + target = target[1 : len(target)-1] + } + err = rc.SetTargetTXT(target) + + case "SRV": + // Infomaniak returns SRV as "priority weight port target" + rc.Type = rtype + err = rc.SetTargetSRVString(addTrailingDot(target)) + + case "CAA": + // Infomaniak returns CAA as "flags tag value" (e.g., "0 issue letsencrypt.org") + rc.Type = rtype + err = rc.SetTargetCAAString(target) + + case "DS": + // Infomaniak returns DS as "keytag algorithm digesttype digest" + // Note: Infomaniak may split long digest data with spaces, so we need to rejoin them + rc.Type = rtype + parts := strings.Fields(target) + if len(parts) >= 4 { + // Rejoin all parts after the first 3 (keytag, algorithm, digesttype) as the digest + digest := strings.Join(parts[3:], "") + target = fmt.Sprintf("%s %s %s %s", parts[0], parts[1], parts[2], digest) + } + err = rc.SetTargetDSString(target) + + case "SSHFP": + // Infomaniak returns SSHFP as "algorithm fingerprint_type fingerprint" + // Note: Infomaniak may split long fingerprint data with spaces, so we need to rejoin them + rc.Type = rtype + parts := strings.Fields(target) + if len(parts) >= 3 { + // Rejoin all parts after the first 2 (algorithm, fingerprint_type) as the fingerprint + fingerprint := strings.Join(parts[2:], "") + target = fmt.Sprintf("%s %s %s", parts[0], parts[1], fingerprint) + } + err = rc.SetTargetSSHFPString(target) + + case "TLSA": + // Infomaniak returns TLSA as "usage selector matching_type certificate" + // Note: Infomaniak may split long certificate data with spaces, so we need to rejoin them + rc.Type = rtype + parts := strings.Fields(target) + if len(parts) >= 4 { + // Rejoin all parts after the first 3 (usage, selector, matching_type) as the certificate + certificate := strings.Join(parts[3:], "") + target = fmt.Sprintf("%s %s %s %s", parts[0], parts[1], parts[2], certificate) + } + err = rc.SetTargetTLSAString(target) + + default: + rc.Type = rtype + err = rc.SetTarget(target) + } + + if err != nil { + return nil, fmt.Errorf("unparsable record type=%q target=%q received from Infomaniak: %w", rtype, target, err) + } + + return rc, nil +} + +// fromRecordConfig converts a RecordConfig to the API format for creation. +func fromRecordConfig(rc *models.RecordConfig) *dnsRecordCreate { + // Get the label - Infomaniak uses empty string for apex + label := rc.GetLabel() + if label == "@" { + label = "" + } + + // Get the target in the format expected by Infomaniak API + var target string + switch rc.Type { + case "A", "AAAA": + target = rc.GetTargetField() + + case "CNAME", "NS", "DNAME": + // Remove trailing dot for the API + target = strings.TrimSuffix(rc.GetTargetField(), ".") + + case "MX": + // Format: "priority target" (without trailing dot) + target = fmt.Sprintf("%d %s", rc.MxPreference, strings.TrimSuffix(rc.GetTargetField(), ".")) + + case "TXT": + target = rc.GetTargetField() + + case "SRV": + // Format: "priority weight port target" (without trailing dot) + target = fmt.Sprintf("%d %d %d %s", rc.SrvPriority, rc.SrvWeight, rc.SrvPort, strings.TrimSuffix(rc.GetTargetField(), ".")) + + case "CAA": + // Format: "flags tag value" + target = fmt.Sprintf("%d %s %s", rc.CaaFlag, rc.CaaTag, rc.GetTargetField()) + + case "DS": + // Format: "keytag algorithm digesttype digest" + target = fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest) + + case "SSHFP": + // Format: "algorithm fingerprint_type fingerprint" + target = fmt.Sprintf("%d %d %s", rc.SshfpAlgorithm, rc.SshfpFingerprint, rc.GetTargetField()) + + case "TLSA": + // Format: "usage selector matching_type certificate" + target = fmt.Sprintf("%d %d %d %s", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType, rc.GetTargetField()) + + default: + target = rc.GetTargetField() + } + + return &dnsRecordCreate{ + Source: label, + Type: rc.Type, + TTL: int64(rc.TTL), + Target: target, + } +} + +// toRecordUpdate converts a RecordConfig to the API format for updating. +func toRecordUpdate(rc *models.RecordConfig) *dnsRecordUpdate { + // Get the target in the format expected by Infomaniak API + var target string + switch rc.Type { + case "A", "AAAA": + target = rc.GetTargetField() + + case "CNAME", "NS", "DNAME": + // Remove trailing dot for the API + target = strings.TrimSuffix(rc.GetTargetField(), ".") + + case "MX": + // Format: "priority target" (without trailing dot) + target = fmt.Sprintf("%d %s", rc.MxPreference, strings.TrimSuffix(rc.GetTargetField(), ".")) + + case "TXT": + target = rc.GetTargetField() + + case "SRV": + // Format: "priority weight port target" (without trailing dot) + target = fmt.Sprintf("%d %d %d %s", rc.SrvPriority, rc.SrvWeight, rc.SrvPort, strings.TrimSuffix(rc.GetTargetField(), ".")) + + case "CAA": + // Format: "flags tag value" + target = fmt.Sprintf("%d %s %s", rc.CaaFlag, rc.CaaTag, rc.GetTargetField()) + + case "DS": + // Format: "keytag algorithm digesttype digest" + target = fmt.Sprintf("%d %d %d %s", rc.DsKeyTag, rc.DsAlgorithm, rc.DsDigestType, rc.DsDigest) + + case "SSHFP": + // Format: "algorithm fingerprint_type fingerprint" + target = fmt.Sprintf("%d %d %s", rc.SshfpAlgorithm, rc.SshfpFingerprint, rc.GetTargetField()) + + case "TLSA": + // Format: "usage selector matching_type certificate" + target = fmt.Sprintf("%d %d %d %s", rc.TlsaUsage, rc.TlsaSelector, rc.TlsaMatchingType, rc.GetTargetField()) + + default: + target = rc.GetTargetField() + } + + return &dnsRecordUpdate{ + TTL: int64(rc.TTL), + Target: target, + } +} + func (p *infomaniakProvider) GetZoneRecords(domain string, meta map[string]string) (models.Records, error) { records, err := p.getDNSRecords(domain) if err != nil { return nil, err } - cleanRecords := make(models.Records, 0) + cleanRecords := make(models.Records, 0, len(records)) for _, r := range records { - recConfig := &models.RecordConfig{ - Original: r, - TTL: uint32(r.TTL), - Type: r.Type, + recConfig, err := toRecordConfig(domain, r) + if err != nil { + return nil, err } - recConfig.SetLabelFromFQDN(r.Source, domain) - recConfig.SetTarget(r.Target) - cleanRecords = append(cleanRecords, recConfig) } @@ -83,6 +297,7 @@ func (p *infomaniakProvider) GetZoneRecords(domain string, meta map[string]strin func (p *infomaniakProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, existingRecords models.Records) ([]*models.Correction, int, error) { var corrections []*models.Correction + domain := dc.Name changes, actualChangeCount, err := diff2.ByRecord(existingRecords, dc, nil) if err != nil { @@ -94,28 +309,32 @@ func (p *infomaniakProvider) GetZoneRecordsCorrections(dc *models.DomainConfig, case diff2.REPORT: corrections = append(corrections, &models.Correction{Msg: change.MsgsJoined}) case diff2.CHANGE: - fmt.Printf("CHANGE: %+v\n", change.New) - // corrections = append(corrections, &models.Correction{ - // Msg: change.Msgs[0], - // F: func() error { - // return p.updateRecord(change.Old[0].Original.(dnsRecord), change.New[0], dc.Name) - // }, - // }) + oldRec := change.Old[0].Original.(dnsRecord) + newRec := change.New[0] + corrections = append(corrections, &models.Correction{ + Msg: change.MsgsJoined, + F: func() error { + _, err := p.updateDNSRecord(domain, fmt.Sprintf("%v", oldRec.ID), toRecordUpdate(newRec)) + return err + }, + }) + case diff2.CREATE: - fmt.Printf("CREATE: %+v\n", change.New) - // corrections = append(corrections, &models.Correction{ - // Msg: change.Msgs[0], - // F: func() error { - // _, err := p.createDNSRecord(dc.Name, change.New[0]) - // return err - // }, - // }) + rec := change.New[0] + corrections = append(corrections, &models.Correction{ + Msg: change.MsgsJoined, + F: func() error { + _, err := p.createDNSRecord(domain, fromRecordConfig(rec)) + return err + }, + }) + case diff2.DELETE: rec := change.Old[0].Original.(dnsRecord) corrections = append(corrections, &models.Correction{ - Msg: change.Msgs[0], + Msg: change.MsgsJoined, F: func() error { - return p.deleteDNSRecord(dc.Name, fmt.Sprintf("%v", rec.ID)) + return p.deleteDNSRecord(domain, fmt.Sprintf("%v", rec.ID)) }, }) }