Skip to content

Commit 195a13d

Browse files
Add OVH provider
1 parent e4ad7c2 commit 195a13d

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
_ "github.com/rancher/external-dns/providers/dnsimple"
1717
_ "github.com/rancher/external-dns/providers/gandi"
1818
_ "github.com/rancher/external-dns/providers/infoblox"
19+
_ "github.com/rancher/external-dns/providers/ovh"
1920
_ "github.com/rancher/external-dns/providers/pointhq"
2021
_ "github.com/rancher/external-dns/providers/powerdns"
2122
_ "github.com/rancher/external-dns/providers/rfc2136"

providers/ovh/ovh.go

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
package OVH
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strconv"
7+
"strings"
8+
9+
"github.com/Sirupsen/logrus"
10+
api "github.com/ovh/go-ovh/ovh"
11+
"github.com/rancher/external-dns/providers"
12+
"github.com/rancher/external-dns/utils"
13+
)
14+
15+
type Record struct {
16+
Target string `json:"target"`
17+
TTL int64 `json:"ttl"`
18+
Zone string `json:"zone"`
19+
FieldType string `json:"fieldType"`
20+
ID int64 `json:"id"`
21+
SubDomain string `json:"subDomain"`
22+
}
23+
24+
type OVHProvider struct {
25+
client *api.Client
26+
root string
27+
}
28+
29+
func init() {
30+
providers.RegisterProvider("ovh", &OVHProvider{})
31+
}
32+
33+
func (d *OVHProvider) Init(rootDomainName string) error {
34+
var endpoint, applicationKey, applicationSecret, consumerKey string
35+
if endpoint = os.Getenv("OVH_ENDPOINT"); len(endpoint) == 0 {
36+
return fmt.Errorf("OVH_ENDPOINT is not set")
37+
}
38+
39+
if applicationKey = os.Getenv("OVH_APPLICATION_KEY"); len(applicationKey) == 0 {
40+
return fmt.Errorf("OVH_APPLICATION_KEY is not set")
41+
}
42+
43+
if applicationSecret = os.Getenv("OVH_APPLICATION_SECRET"); len(applicationSecret) == 0 {
44+
return fmt.Errorf("OVH_APPLICATION_SECRET is not set")
45+
}
46+
47+
if consumerKey = os.Getenv("OVH_CONSUMER_KEY"); len(consumerKey) == 0 {
48+
return fmt.Errorf("OVH_CONSUMER_KEY is not set")
49+
}
50+
51+
d.root = utils.UnFqdn(rootDomainName)
52+
client, err := api.NewClient(endpoint, applicationKey, applicationSecret, consumerKey)
53+
if err != nil {
54+
return fmt.Errorf("Failed to create OVH client: %v", err)
55+
}
56+
57+
d.client = client
58+
59+
var zones []string
60+
err = d.client.Get("/domain/zone", &zones)
61+
62+
if err != nil {
63+
return fmt.Errorf("Failed to list hosted zones: %v", err)
64+
}
65+
66+
found := false
67+
for _, zone := range zones {
68+
if zone == d.root {
69+
found = true
70+
break
71+
}
72+
}
73+
74+
if !found {
75+
return fmt.Errorf("Zone for '%s' not found", d.root)
76+
}
77+
78+
logrus.Infof("Configured %s with zone '%s'", d.GetName(), d.root)
79+
return nil
80+
}
81+
82+
func (*OVHProvider) GetName() string {
83+
return "OVH"
84+
}
85+
86+
func (d *OVHProvider) HealthCheck() error {
87+
var me interface{}
88+
err := d.client.Get("/me", &me)
89+
return err
90+
}
91+
92+
func (d *OVHProvider) parseName(record utils.DnsRecord) string {
93+
name := strings.TrimSuffix(record.Fqdn, fmt.Sprintf(".%s.", d.root))
94+
return name
95+
}
96+
97+
func (d *OVHProvider) AddRecord(record utils.DnsRecord) (err error) {
98+
var url string
99+
var body interface{}
100+
var resType interface{}
101+
for _, rec := range record.Records {
102+
if url, body, err = d.prepareRecord(rec, record.Type, record.Fqdn, record.TTL); err != nil {
103+
return err
104+
}
105+
if err = d.client.Post(url, body, resType); err != nil {
106+
return fmt.Errorf("OVH API call `POST %s` with body `%s` has failed: %v", url, body, err)
107+
}
108+
}
109+
d.refreshZone()
110+
return nil
111+
}
112+
113+
func (d *OVHProvider) FindRecords(record utils.DnsRecord) ([]*Record, error) {
114+
var records []*Record
115+
116+
urlRecIDs := strings.Join([]string{"/domain/zone/", d.root, "/record"}, "")
117+
118+
var recIDs []int64
119+
err := d.client.Get(urlRecIDs, &recIDs)
120+
121+
if err != nil {
122+
return records, fmt.Errorf("OVH API call `GET %s` has failed: %v", urlRecIDs, err)
123+
}
124+
125+
name := d.parseName(record)
126+
for _, recID := range recIDs {
127+
urlRecord := strings.Join([]string{"/domain/zone/", d.root, "/record/", strconv.FormatInt(recID, 10)}, "")
128+
var rec *Record
129+
if err = d.client.Get(urlRecord, &rec); err != nil {
130+
return records, fmt.Errorf("OVH API call `GET %s` has failed: %v", urlRecord, err)
131+
}
132+
133+
if rec.SubDomain == name && rec.FieldType == record.Type {
134+
records = append(records, rec)
135+
}
136+
}
137+
138+
return records, nil
139+
}
140+
141+
func (d *OVHProvider) UpdateRecord(record utils.DnsRecord) error {
142+
err := d.RemoveRecord(record)
143+
if err != nil {
144+
return err
145+
}
146+
147+
return d.AddRecord(record)
148+
}
149+
150+
func (d *OVHProvider) RemoveRecord(record utils.DnsRecord) error {
151+
records, err := d.FindRecords(record)
152+
if err != nil {
153+
return err
154+
}
155+
156+
for _, rec := range records {
157+
urlRecord := strings.Join([]string{"/domain/zone/", d.root, "/record/", strconv.FormatInt(rec.ID, 10)}, "")
158+
var resType interface{}
159+
if err := d.client.Delete(urlRecord, &resType); err != nil {
160+
return fmt.Errorf("OVH API call `DELETE %s` has failed: %v", urlRecord, err)
161+
}
162+
}
163+
164+
d.refreshZone()
165+
166+
return nil
167+
}
168+
169+
func (d *OVHProvider) GetRecords() ([]utils.DnsRecord, error) {
170+
var dnsRecords []utils.DnsRecord
171+
172+
urlRecIDs := strings.Join([]string{"/domain/zone/", d.root, "/record"}, "")
173+
174+
var recIDs []int64
175+
err := d.client.Get(urlRecIDs, &recIDs)
176+
177+
if err != nil {
178+
return dnsRecords, fmt.Errorf("OVH API call `GET %s` has failed: %v", urlRecIDs, err)
179+
}
180+
181+
var records []*Record
182+
for _, recID := range recIDs {
183+
urlRecord := strings.Join([]string{"/domain/zone/", d.root, "/record/", strconv.FormatInt(recID, 10)}, "")
184+
var record *Record
185+
if err = d.client.Get(urlRecord, &record); err != nil {
186+
return dnsRecords, fmt.Errorf("OVH API call `GET %s` has failed: %v", urlRecord, err)
187+
}
188+
records = append(records, record)
189+
}
190+
191+
recordMap := map[string]map[string][]string{}
192+
recordTTLs := map[string]map[string]int{}
193+
194+
for _, rec := range records {
195+
var fqdn string
196+
197+
if rec.SubDomain == "" {
198+
fqdn = fmt.Sprintf("%s.", rec.Zone)
199+
} else {
200+
fqdn = fmt.Sprintf("%s.%s.", rec.SubDomain, rec.Zone)
201+
}
202+
203+
recordTTLs[fqdn] = map[string]int{}
204+
recordTTLs[fqdn][rec.FieldType] = int(rec.TTL)
205+
recordSet, exists := recordMap[fqdn]
206+
207+
if exists {
208+
recordSlice, sliceExists := recordSet[rec.FieldType]
209+
if sliceExists {
210+
recordSlice = append(recordSlice, rec.Target)
211+
recordSet[rec.FieldType] = recordSlice
212+
} else {
213+
recordSet[rec.FieldType] = []string{rec.Target}
214+
}
215+
} else {
216+
recordMap[fqdn] = map[string][]string{}
217+
recordMap[fqdn][rec.FieldType] = []string{rec.Target}
218+
}
219+
}
220+
221+
for fqdn, recordSet := range recordMap {
222+
for recordType, recordSlice := range recordSet {
223+
ttl := recordTTLs[fqdn][recordType]
224+
record := utils.DnsRecord{Fqdn: fqdn, Records: recordSlice, Type: recordType, TTL: ttl}
225+
dnsRecords = append(dnsRecords, record)
226+
}
227+
}
228+
229+
return dnsRecords, nil
230+
}
231+
232+
func (d *OVHProvider) prepareRecord(rec string, tp string, fqdn string, ttl int) (string, interface{}, error) {
233+
var url string
234+
url = strings.Join([]string{"/domain/zone/", d.root, "/record"}, "")
235+
body := make(map[string]interface{})
236+
name := strings.TrimSuffix(fqdn, fmt.Sprintf(".%s.", d.root))
237+
body["fieldType"] = tp
238+
body["subDomain"] = name
239+
body["ttl"] = ttl
240+
body["target"] = rec
241+
return url, body, nil
242+
}
243+
244+
func (d *OVHProvider) refreshZone() error {
245+
url := strings.Join([]string{"/domain/zone/", d.root, "/refresh"}, "")
246+
var resType interface{}
247+
if err := d.client.Post(url, nil, &resType); err != nil {
248+
return fmt.Errorf("OVH API call `POST %s` has failed: %v", url, err)
249+
}
250+
return nil
251+
}

vendor.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ github.com/jmespath/go-jmespath 0b12d6b
1818
github.com/juju/ratelimit 77ed1c8
1919
github.com/kolo/xmlrpc 0826b98
2020
github.com/miekg/dns 48ab660
21+
github.com/ovh/go-ovh df6beeb
2122
github.com/pkg/errors ff09b13
2223
github.com/prasmussen/gandi-api 2dd22da
2324
github.com/rancher/go-rancher-metadata/metadata 11a77c2

0 commit comments

Comments
 (0)