Skip to content

[WIP] INFOMANIAK provider #3574

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
1 change: 1 addition & 0 deletions OWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Currently supported DNS providers:
- hosting.de
- Huawei Cloud DNS
- Hurricane Electric DNS
- Infomaniak
- INWX
- Linode
- Loopia
Expand Down
1 change: 1 addition & 0 deletions documentation/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
38 changes: 38 additions & 0 deletions documentation/provider/infomaniak.md
Original file line number Diff line number Diff line change
@@ -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
{
"infomaniak": {
"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_NONE = NewRegistrar("none");
var DSP_INFOMANIAK = NewDnsProvider("infomaniak");

D("example.com", REG_NONE, DnsProvider(DSP_INFOMANIAK),
A("test", "1.2.3.4"),
);
```
{% endcode %}

## Activation
DNSControl depends on a Infomaniak account personal access token.
5 changes: 5 additions & 0 deletions integrationTest/profiles.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@
"TYPE": "HUAWEICLOUD",
"domain": "$HUAWEICLOUD_DOMAIN"
},
"INFOMANIAK": {
"TYPE": "INFOMANIAK",
"domain": "$INFOMANIAK_DOMAIN",
"token": "$INFOMANIAK_TOKEN"
},
"INWX": {
"TYPE": "INWX",
"domain": "$INWX_DOMAIN",
Expand Down
1 change: 1 addition & 0 deletions providers/_all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
186 changes: 186 additions & 0 deletions providers/infomaniak/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package infomaniak

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)

const baseURL = "https://api.infomaniak.com/2"

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,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,omitempty"`
Error errorRecord `json:"error,omitempty"`
}

type boolResponse struct {
Result string `json:"result"`
Data bool `json:"data,omitempty"`
Error errorRecord `json:"error,omitempty"`
}
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 int64 `json:"id,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"`
}

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)

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
}
defer res.Body.Close()

response := &dnsZoneResponse{}
err = json.NewDecoder(res.Body).Decode(response)
if err != nil {
return nil, err
}

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)

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
}
defer res.Body.Close()

response := &dnsRecordsResponse{}
err = json.NewDecoder(res.Body).Decode(response)
if err != nil {
return nil, err
}

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, 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 {
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()

response := &boolResponse{}
err = json.NewDecoder(res.Body).Decode(response)
if err != nil {
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
}

// 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
}

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()

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 create %s record in zone %s: %s", rec.Type, zone, response.Error.Description)
}

return &response.Data, nil
}
15 changes: 15 additions & 0 deletions providers/infomaniak/auditrecords.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading