mirror of
https://github.com/github/octodns.git
synced 2024-05-11 05:55:00 +00:00
Merge remote-tracking branch 'origin/master' into overriding-yaml-provider
This commit is contained in:
@@ -19,7 +19,7 @@ a:
|
||||
- value: 6.6.6.6
|
||||
weight: 10
|
||||
- value: 5.5.5.5
|
||||
weight: 25
|
||||
weight: 15
|
||||
rules:
|
||||
- geos:
|
||||
- EU-GB
|
||||
@@ -90,9 +90,9 @@ cname:
|
||||
sea:
|
||||
values:
|
||||
- value: target-sea-1.unit.tests.
|
||||
weight: 100
|
||||
weight: 10
|
||||
- value: target-sea-2.unit.tests.
|
||||
weight: 175
|
||||
weight: 14
|
||||
rules:
|
||||
- geos:
|
||||
- EU-GB
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
providers:
|
||||
yaml:
|
||||
class: octodns.provider.yaml.YamlProvider
|
||||
directory: ./config
|
||||
simple_source:
|
||||
class: helpers.SimpleSource
|
||||
zones:
|
||||
missing.sources.:
|
||||
targets:
|
||||
- yaml
|
||||
missing.targets.:
|
||||
sources:
|
||||
- yaml
|
||||
unknown.source.:
|
||||
sources:
|
||||
- not-there
|
||||
targets:
|
||||
- yaml
|
||||
unknown.target.:
|
||||
sources:
|
||||
- yaml
|
||||
targets:
|
||||
- not-there-either
|
||||
not.targetable.:
|
||||
sources:
|
||||
- yaml
|
||||
targets:
|
||||
- simple_source
|
||||
@@ -23,7 +23,7 @@ a:
|
||||
fallback: null
|
||||
values:
|
||||
- value: 5.5.5.5
|
||||
weight: 25
|
||||
weight: 15
|
||||
- value: 6.6.6.6
|
||||
weight: 10
|
||||
rules:
|
||||
|
||||
@@ -21,9 +21,9 @@ cname:
|
||||
fallback: null
|
||||
values:
|
||||
- value: target-sea-1.unit.tests.
|
||||
weight: 100
|
||||
weight: 10
|
||||
- value: target-sea-2.unit.tests.
|
||||
weight: 175
|
||||
weight: 14
|
||||
rules:
|
||||
- geos:
|
||||
- EU-GB
|
||||
|
||||
@@ -5,24 +5,8 @@ providers:
|
||||
simple_source:
|
||||
class: helpers.SimpleSource
|
||||
zones:
|
||||
missing.sources.:
|
||||
targets:
|
||||
- yaml
|
||||
missing.targets.:
|
||||
sources:
|
||||
- yaml
|
||||
unknown.source.:
|
||||
sources:
|
||||
- not-there
|
||||
targets:
|
||||
- yaml
|
||||
unknown.target.:
|
||||
sources:
|
||||
- yaml
|
||||
targets:
|
||||
- not-there-either
|
||||
not.targetable.:
|
||||
sources:
|
||||
- yaml
|
||||
targets:
|
||||
- simple_source
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
[{
|
||||
"id": 123123,
|
||||
"name": "unit.tests",
|
||||
"soa": {
|
||||
"primaryNameserver": "ns11.constellix.com.",
|
||||
"email": "dns.constellix.com.",
|
||||
"ttl": 86400,
|
||||
"serial": 2015010102,
|
||||
"refresh": 43200,
|
||||
"retry": 3600,
|
||||
"expire": 1209600,
|
||||
"negCache": 180
|
||||
},
|
||||
"createdTs": "2019-08-07T03:36:02Z",
|
||||
"modifiedTs": "2019-08-07T03:36:02Z",
|
||||
"typeId": 1,
|
||||
"domainTags": [],
|
||||
"folder": null,
|
||||
"hasGtdRegions": false,
|
||||
"hasGeoIP": false,
|
||||
"nameserverGroup": 1,
|
||||
"nameservers": ["ns11.constellix.com.", "ns21.constellix.com.", "ns31.constellix.com.", "ns41.constellix.net.", "ns51.constellix.net.", "ns61.constellix.net."],
|
||||
"note": "",
|
||||
"version": 0,
|
||||
"status": "ACTIVE",
|
||||
"tags": [],
|
||||
"contactIds": []
|
||||
}]
|
||||
+598
@@ -0,0 +1,598 @@
|
||||
[{
|
||||
"id": 1808529,
|
||||
"type": "CAA",
|
||||
"recordType": "caa",
|
||||
"name": "",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 3600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149569216,
|
||||
"value": [{
|
||||
"flag": 0,
|
||||
"tag": "issue",
|
||||
"data": "ca.unit.tests",
|
||||
"caaProviderId": 1,
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"flag": 0,
|
||||
"tag": "issue",
|
||||
"data": "ca.unit.tests",
|
||||
"caaProviderId": 1,
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808516,
|
||||
"type": "A",
|
||||
"recordType": "a",
|
||||
"name": "",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149623640,
|
||||
"value": ["1.2.3.4", "1.2.3.5"],
|
||||
"roundRobin": [{
|
||||
"value": "1.2.3.4",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "1.2.3.5",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"roundRobinFailover": [],
|
||||
"pools": [],
|
||||
"poolsDetail": []
|
||||
}, {
|
||||
"id": 1808527,
|
||||
"type": "SRV",
|
||||
"recordType": "srv",
|
||||
"name": "_srv._tcp",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149714387,
|
||||
"value": [{
|
||||
"value": "foo-1.unit.tests.",
|
||||
"priority": 10,
|
||||
"weight": 20,
|
||||
"port": 30,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "foo-2.unit.tests.",
|
||||
"priority": 12,
|
||||
"weight": 20,
|
||||
"port": 30,
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "foo-1.unit.tests.",
|
||||
"priority": 10,
|
||||
"weight": 20,
|
||||
"port": 30,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "foo-2.unit.tests.",
|
||||
"priority": 12,
|
||||
"weight": 20,
|
||||
"port": 30,
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808515,
|
||||
"type": "AAAA",
|
||||
"recordType": "aaaa",
|
||||
"name": "aaaa",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149739464,
|
||||
"value": ["2601:644:500:e210:62f8:1dff:feb8:947a"],
|
||||
"roundRobin": [{
|
||||
"value": "2601:644:500:e210:62f8:1dff:feb8:947a",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"pools": [],
|
||||
"poolsDetail": [],
|
||||
"roundRobinFailover": []
|
||||
}, {
|
||||
"id": 1808530,
|
||||
"type": "ANAME",
|
||||
"recordType": "aname",
|
||||
"name": "",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 1800,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150251379,
|
||||
"value": [{
|
||||
"value": "aname.unit.tests.",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "aname.unit.tests.",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"pools": [],
|
||||
"poolsDetail": []
|
||||
}, {
|
||||
"id": 1808521,
|
||||
"type": "CNAME",
|
||||
"recordType": "cname",
|
||||
"name": "cname",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565152113825,
|
||||
"value": "",
|
||||
"roundRobin": [{
|
||||
"value": "",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": [{
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 1,
|
||||
"markedActive": false
|
||||
}, {
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 2,
|
||||
"markedActive": false
|
||||
}]
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": [{
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 1,
|
||||
"markedActive": false
|
||||
}, {
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 2,
|
||||
"markedActive": false
|
||||
}]
|
||||
},
|
||||
"pools": [],
|
||||
"poolsDetail": [],
|
||||
"geolocation": null,
|
||||
"host": ""
|
||||
}, {
|
||||
"id": 1808522,
|
||||
"type": "CNAME",
|
||||
"recordType": "cname",
|
||||
"name": "included",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 3600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565152119137,
|
||||
"value": "",
|
||||
"roundRobin": [{
|
||||
"value": "",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": [{
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 1,
|
||||
"markedActive": false
|
||||
}, {
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 2,
|
||||
"markedActive": false
|
||||
}]
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": [{
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 1,
|
||||
"markedActive": false
|
||||
}, {
|
||||
"id": null,
|
||||
"value": "",
|
||||
"disableFlag": false,
|
||||
"failedFlag": false,
|
||||
"status": "N/A",
|
||||
"sortOrder": 2,
|
||||
"markedActive": false
|
||||
}]
|
||||
},
|
||||
"pools": [],
|
||||
"poolsDetail": [],
|
||||
"geolocation": null,
|
||||
"host": ""
|
||||
}, {
|
||||
"id": 1808523,
|
||||
"type": "MX",
|
||||
"recordType": "mx",
|
||||
"name": "mx",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149879856,
|
||||
"value": [{
|
||||
"value": "smtp-3.unit.tests.",
|
||||
"level": 30,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-2.unit.tests.",
|
||||
"level": 20,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-4.unit.tests.",
|
||||
"level": 10,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-1.unit.tests.",
|
||||
"level": 40,
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "smtp-3.unit.tests.",
|
||||
"level": 30,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-2.unit.tests.",
|
||||
"level": 20,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-4.unit.tests.",
|
||||
"level": 10,
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "smtp-1.unit.tests.",
|
||||
"level": 40,
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808525,
|
||||
"type": "PTR",
|
||||
"recordType": "ptr",
|
||||
"name": "ptr",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150115139,
|
||||
"value": [{
|
||||
"value": "foo.bar.com.",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "foo.bar.com.",
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808526,
|
||||
"type": "SPF",
|
||||
"recordType": "spf",
|
||||
"name": "spf",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149916132,
|
||||
"value": [{
|
||||
"value": "\"v=spf1 ip4:192.168.0.1/16-all\"",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "\"v=spf1 ip4:192.168.0.1/16-all\"",
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808528,
|
||||
"type": "TXT",
|
||||
"recordType": "txt",
|
||||
"name": "txt",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565149966915,
|
||||
"value": [{
|
||||
"value": "\"Bah bah black sheep\"",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "\"have you any wool.\"",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "\"Bah bah black sheep\"",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "\"have you any wool.\"",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"",
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808524,
|
||||
"type": "NS",
|
||||
"recordType": "ns",
|
||||
"name": "under",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 3600,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150062850,
|
||||
"value": [{
|
||||
"value": "ns1.unit.tests.",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "ns2",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "ns1.unit.tests.",
|
||||
"disableFlag": false
|
||||
}, {
|
||||
"value": "ns2",
|
||||
"disableFlag": false
|
||||
}]
|
||||
}, {
|
||||
"id": 1808531,
|
||||
"type": "HTTPRedirection",
|
||||
"recordType": "httpredirection",
|
||||
"name": "unsupported",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150348154,
|
||||
"value": "https://redirect.unit.tests",
|
||||
"roundRobin": [{
|
||||
"value": "https://redirect.unit.tests"
|
||||
}],
|
||||
"title": "Unsupported Record",
|
||||
"keywords": "unsupported",
|
||||
"description": "unsupported record",
|
||||
"hardlinkFlag": false,
|
||||
"redirectTypeId": 1,
|
||||
"url": "https://redirect.unit.tests"
|
||||
}, {
|
||||
"id": 1808519,
|
||||
"type": "A",
|
||||
"recordType": "a",
|
||||
"name": "www",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150079027,
|
||||
"value": ["2.2.3.6"],
|
||||
"roundRobin": [{
|
||||
"value": "2.2.3.6",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"roundRobinFailover": [],
|
||||
"pools": [],
|
||||
"poolsDetail": []
|
||||
}, {
|
||||
"id": 1808603,
|
||||
"type": "ANAME",
|
||||
"recordType": "aname",
|
||||
"name": "sub",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 1800,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565153387855,
|
||||
"value": [{
|
||||
"value": "aname.unit.tests.",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"roundRobin": [{
|
||||
"value": "aname.unit.tests.",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"pools": [],
|
||||
"poolsDetail": []
|
||||
}, {
|
||||
"id": 1808520,
|
||||
"type": "A",
|
||||
"recordType": "a",
|
||||
"name": "www.sub",
|
||||
"recordOption": "roundRobin",
|
||||
"noAnswer": false,
|
||||
"note": "",
|
||||
"ttl": 300,
|
||||
"gtdRegion": 1,
|
||||
"parentId": 123123,
|
||||
"parent": "domain",
|
||||
"source": "Domain",
|
||||
"modifiedTs": 1565150090588,
|
||||
"value": ["2.2.3.6"],
|
||||
"roundRobin": [{
|
||||
"value": "2.2.3.6",
|
||||
"disableFlag": false
|
||||
}],
|
||||
"geolocation": null,
|
||||
"recordFailover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"failover": {
|
||||
"disabled": false,
|
||||
"failoverType": 1,
|
||||
"failoverTypeStr": "Normal (always lowest level)",
|
||||
"values": []
|
||||
},
|
||||
"roundRobinFailover": [],
|
||||
"pools": [],
|
||||
"poolsDetail": []
|
||||
}]
|
||||
+11
@@ -1,5 +1,16 @@
|
||||
{
|
||||
"domain_records": [{
|
||||
"id": null,
|
||||
"type": "SOA",
|
||||
"name": "@",
|
||||
"data": null,
|
||||
"priority": null,
|
||||
"port": null,
|
||||
"ttl": null,
|
||||
"weight": null,
|
||||
"flags": null,
|
||||
"tag": null
|
||||
}, {
|
||||
"id": 11189874,
|
||||
"type": "NS",
|
||||
"name": "@",
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"recordsets": [
|
||||
{
|
||||
"rdata": [
|
||||
"",
|
||||
"12 20 foo-2.unit.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"",
|
||||
"1 1"
|
||||
],
|
||||
"type": "SSHFP",
|
||||
"name": "unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"",
|
||||
"100 \"U\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" ."
|
||||
],
|
||||
"type": "NAPTR",
|
||||
"name": "naptr.unit.tests",
|
||||
"ttl": 600
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"totalElements": 3,
|
||||
"showAll": true
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"recordsets": [
|
||||
{
|
||||
"rdata": [
|
||||
"10 20 30 foo-1.other.tests.",
|
||||
"12 20 30 foo-2.other.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 20 30 foo-1.other.tests.",
|
||||
"12 20 30 foo-2.other.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2601:644:500:e210:62f8:1dff:feb8:9471"
|
||||
],
|
||||
"type": "AAAA",
|
||||
"name": "aaaa.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net.",
|
||||
"ns2.akam.net.",
|
||||
"ns3.akam.net.",
|
||||
"ns4.akam.net."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "old.other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1.2.3.4",
|
||||
"1.2.3.5"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "old.other.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300"
|
||||
],
|
||||
"type": "SOA",
|
||||
"name": "other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49",
|
||||
"1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73"
|
||||
],
|
||||
"type": "SSHFP",
|
||||
"name": "old.other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"other.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "old.cname.other.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"other.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "excluded.old.other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"other.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "included.old.other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 smtp-4.other.tests.",
|
||||
"20 smtp-2.other.tests.",
|
||||
"30 smtp-3.other.tests.",
|
||||
"40 smtp-1.other.tests."
|
||||
],
|
||||
"type": "MX",
|
||||
"name": "mx.old.other.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" .",
|
||||
"100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" ."
|
||||
],
|
||||
"type": "NAPTR",
|
||||
"name": "naptr.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"foo.bar.com."
|
||||
],
|
||||
"type": "PTR",
|
||||
"name": "ptr.old.other.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"v=spf1 ip4:192.168.0.1/16-all\""
|
||||
],
|
||||
"type": "SPF",
|
||||
"name": "spf.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.other.tests.",
|
||||
"ns2.other.tests."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "under.old.other.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"Bah bah black sheep\"",
|
||||
"\"have you any wool.\"",
|
||||
"\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\""
|
||||
],
|
||||
"type": "TXT",
|
||||
"name": "txt.old.other.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.7"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.other.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.6"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.sub.old.other.tests",
|
||||
"ttl": 300
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"totalElements": 16,
|
||||
"showAll": true
|
||||
}
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
{
|
||||
"recordsets": [
|
||||
{
|
||||
"rdata": [
|
||||
"10 20 30 foo-1.unit.tests.",
|
||||
"12 20 30 foo-2.unit.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 20 30 foo-1.unit.tests.",
|
||||
"12 20 30 foo-2.unit.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2601:644:500:e210:62f8:1dff:feb8:9471"
|
||||
],
|
||||
"type": "AAAA",
|
||||
"name": "aaaa.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net.",
|
||||
"ns2.akam.net.",
|
||||
"ns3.akam.net.",
|
||||
"ns4.akam.net."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "old.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1.2.3.4",
|
||||
"1.2.3.5"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "old.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300"
|
||||
],
|
||||
"type": "SOA",
|
||||
"name": "unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49",
|
||||
"1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73"
|
||||
],
|
||||
"type": "SSHFP",
|
||||
"name": "old.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests"
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "old.cname.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "excluded.old.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "included.old.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 smtp-4.unit.tests.",
|
||||
"20 smtp-2.unit.tests.",
|
||||
"30 smtp-3.unit.tests.",
|
||||
"40 smtp-1.unit.tests."
|
||||
],
|
||||
"type": "MX",
|
||||
"name": "mx.old.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" .",
|
||||
"100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" ."
|
||||
],
|
||||
"type": "NAPTR",
|
||||
"name": "naptr.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"foo.bar.com."
|
||||
],
|
||||
"type": "PTR",
|
||||
"name": "ptr.old.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"v=spf1 ip4:192.168.0.1/16-all\""
|
||||
],
|
||||
"type": "SPF",
|
||||
"name": "spf.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.unit.tests.",
|
||||
"ns2.unit.tests."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "under.old.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"Bah bah black sheep\"",
|
||||
"\"have you any wool.\"",
|
||||
"\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\""
|
||||
],
|
||||
"type": "TXT",
|
||||
"name": "txt.old.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.7"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.6"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.sub.old.unit.tests",
|
||||
"ttl": 300
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"totalElements": 16,
|
||||
"showAll": true
|
||||
}
|
||||
}
|
||||
Vendored
+157
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"recordsets": [
|
||||
{
|
||||
"rdata": [
|
||||
"10 20 30 foo-1.unit.tests.",
|
||||
"12 20 30 foo-2.unit.tests."
|
||||
],
|
||||
"type": "SRV",
|
||||
"name": "_srv._tcp.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2601:644:500:e210:62f8:1dff:feb8:947a"
|
||||
],
|
||||
"type": "AAAA",
|
||||
"name": "aaaa.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net.",
|
||||
"ns2.akam.net.",
|
||||
"ns3.akam.net.",
|
||||
"ns4.akam.net."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1.2.3.4",
|
||||
"1.2.3.5"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300"
|
||||
],
|
||||
"type": "SOA",
|
||||
"name": "unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49",
|
||||
"1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73"
|
||||
],
|
||||
"type": "SSHFP",
|
||||
"name": "unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "cname.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "excluded.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"unit.tests."
|
||||
],
|
||||
"type": "CNAME",
|
||||
"name": "included.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 smtp-4.unit.tests.",
|
||||
"20 smtp-2.unit.tests.",
|
||||
"30 smtp-3.unit.tests.",
|
||||
"40 smtp-1.unit.tests."
|
||||
],
|
||||
"type": "MX",
|
||||
"name": "mx.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" .",
|
||||
"100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:[email protected]!\" ."
|
||||
],
|
||||
"type": "NAPTR",
|
||||
"name": "naptr.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"foo.bar.com."
|
||||
],
|
||||
"type": "PTR",
|
||||
"name": "ptr.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"v=spf1 ip4:192.168.0.1/16-all\""
|
||||
],
|
||||
"type": "SPF",
|
||||
"name": "spf.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"ns1.unit.tests.",
|
||||
"ns2.unit.tests."
|
||||
],
|
||||
"type": "NS",
|
||||
"name": "under.unit.tests",
|
||||
"ttl": 3600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"\"Bah bah black sheep\"",
|
||||
"\"have you any wool.\"",
|
||||
"\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\""
|
||||
],
|
||||
"type": "TXT",
|
||||
"name": "txt.unit.tests",
|
||||
"ttl": 600
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.6"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.unit.tests",
|
||||
"ttl": 300
|
||||
},
|
||||
{
|
||||
"rdata": [
|
||||
"2.2.3.6"
|
||||
],
|
||||
"type": "A",
|
||||
"name": "www.sub.unit.tests",
|
||||
"ttl": 300
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"totalElements": 16,
|
||||
"showAll": true
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
@ 3600 NS 6.2.3.4.
|
||||
@ 3600 NS 7.2.3.4.
|
||||
@ 300 A 1.2.3.4
|
||||
@ 300 A 1.2.3.5
|
||||
@ 3600 SSHFP 1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73
|
||||
@ 3600 SSHFP 1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49
|
||||
@ 3600 CAA 0 issue ca.unit.tests
|
||||
_srv._tcp 600 SRV 10 20 30 foo-1.unit.tests.
|
||||
_srv._tcp 600 SRV 12 20 30 foo-2.unit.tests.
|
||||
aaaa 600 AAAA 2601:644:500:e210:62f8:1dff:feb8:947a
|
||||
cname 300 CNAME unit.tests.
|
||||
excluded 300 CNAME unit.tests.
|
||||
ignored 300 A 9.9.9.9
|
||||
included 3600 CNAME unit.tests.
|
||||
mx 300 MX 10 smtp-4.unit.tests.
|
||||
mx 300 MX 20 smtp-2.unit.tests.
|
||||
mx 300 MX 30 smtp-3.unit.tests.
|
||||
mx 300 MX 40 smtp-1.unit.tests.
|
||||
sub 3600 NS 6.2.3.4.
|
||||
sub 3600 NS 7.2.3.4.
|
||||
txt 600 TXT "Bah bah black sheep"
|
||||
txt 600 TXT "have you any wool."
|
||||
txt 600 TXT "v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs"
|
||||
www 300 A 2.2.3.6
|
||||
www.sub 300 A 2.2.3.6
|
||||
@@ -0,0 +1,68 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.equality import EqualityTupleMixin
|
||||
|
||||
|
||||
class TestEqualityTupleMixin(TestCase):
|
||||
|
||||
def test_basics(self):
|
||||
|
||||
class Simple(EqualityTupleMixin):
|
||||
|
||||
def __init__(self, a, b, c):
|
||||
self.a = a
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
||||
def _equality_tuple(self):
|
||||
return (self.a, self.b)
|
||||
|
||||
one = Simple(1, 2, 3)
|
||||
same = Simple(1, 2, 3)
|
||||
matches = Simple(1, 2, 'ignored')
|
||||
doesnt = Simple(2, 3, 4)
|
||||
|
||||
# equality
|
||||
self.assertEquals(one, one)
|
||||
self.assertEquals(one, same)
|
||||
self.assertEquals(same, one)
|
||||
# only a & c are considered
|
||||
self.assertEquals(one, matches)
|
||||
self.assertEquals(matches, one)
|
||||
self.assertNotEquals(one, doesnt)
|
||||
self.assertNotEquals(doesnt, one)
|
||||
|
||||
# lt
|
||||
self.assertTrue(one < doesnt)
|
||||
self.assertFalse(doesnt < one)
|
||||
self.assertFalse(one < same)
|
||||
|
||||
# le
|
||||
self.assertTrue(one <= doesnt)
|
||||
self.assertFalse(doesnt <= one)
|
||||
self.assertTrue(one <= same)
|
||||
|
||||
# gt
|
||||
self.assertFalse(one > doesnt)
|
||||
self.assertTrue(doesnt > one)
|
||||
self.assertFalse(one > same)
|
||||
|
||||
# ge
|
||||
self.assertFalse(one >= doesnt)
|
||||
self.assertTrue(doesnt >= one)
|
||||
self.assertTrue(one >= same)
|
||||
|
||||
def test_not_implemented(self):
|
||||
|
||||
class MissingMethod(EqualityTupleMixin):
|
||||
pass
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
MissingMethod() == MissingMethod()
|
||||
@@ -7,10 +7,12 @@ from __future__ import absolute_import, division, print_function, \
|
||||
|
||||
from os import environ
|
||||
from os.path import dirname, join
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
from octodns.manager import _AggregateTarget, MainThreadExecutor, Manager
|
||||
from octodns.manager import _AggregateTarget, MainThreadExecutor, Manager, \
|
||||
ManagerException
|
||||
from octodns.yaml import safe_load
|
||||
from octodns.zone import Zone
|
||||
|
||||
@@ -27,80 +29,81 @@ def get_config_filename(which):
|
||||
class TestManager(TestCase):
|
||||
|
||||
def test_missing_provider_class(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-provider-class.yaml')).sync()
|
||||
self.assertTrue('missing class' in ctx.exception.message)
|
||||
self.assertTrue('missing class' in text_type(ctx.exception))
|
||||
|
||||
def test_bad_provider_class(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-provider-class.yaml')).sync()
|
||||
self.assertTrue('Unknown provider class' in ctx.exception.message)
|
||||
self.assertTrue('Unknown provider class' in text_type(ctx.exception))
|
||||
|
||||
def test_bad_provider_class_module(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-provider-class-module.yaml')) \
|
||||
.sync()
|
||||
self.assertTrue('Unknown provider class' in ctx.exception.message)
|
||||
self.assertTrue('Unknown provider class' in text_type(ctx.exception))
|
||||
|
||||
def test_bad_provider_class_no_module(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-provider-class-no-module.yaml')) \
|
||||
.sync()
|
||||
self.assertTrue('Unknown provider class' in ctx.exception.message)
|
||||
self.assertTrue('Unknown provider class' in text_type(ctx.exception))
|
||||
|
||||
def test_missing_provider_config(self):
|
||||
# Missing provider config
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-provider-config.yaml')).sync()
|
||||
self.assertTrue('provider config' in ctx.exception.message)
|
||||
self.assertTrue('provider config' in text_type(ctx.exception))
|
||||
|
||||
def test_missing_env_config(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-provider-env.yaml')).sync()
|
||||
self.assertTrue('missing env var' in ctx.exception.message)
|
||||
self.assertTrue('missing env var' in text_type(ctx.exception))
|
||||
|
||||
def test_missing_source(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['missing.sources.'])
|
||||
self.assertTrue('missing sources' in ctx.exception.message)
|
||||
self.assertTrue('missing sources' in text_type(ctx.exception))
|
||||
|
||||
def test_missing_targets(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['missing.targets.'])
|
||||
self.assertTrue('missing targets' in ctx.exception.message)
|
||||
self.assertTrue('missing targets' in text_type(ctx.exception))
|
||||
|
||||
def test_unknown_source(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['unknown.source.'])
|
||||
self.assertTrue('unknown source' in ctx.exception.message)
|
||||
self.assertTrue('unknown source' in text_type(ctx.exception))
|
||||
|
||||
def test_unknown_target(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['unknown.target.'])
|
||||
self.assertTrue('unknown target' in ctx.exception.message)
|
||||
self.assertTrue('unknown target' in text_type(ctx.exception))
|
||||
|
||||
def test_bad_plan_output_class(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
name = 'bad-plan-output-missing-class.yaml'
|
||||
Manager(get_config_filename(name)).sync()
|
||||
self.assertEquals('plan_output bad is missing class',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
def test_bad_plan_output_config(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('bad-plan-output-config.yaml')).sync()
|
||||
self.assertEqual('Incorrect plan_output config for bad',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
def test_source_only_as_a_target(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('provider-problems.yaml')) \
|
||||
.sync(['not.targetable.'])
|
||||
self.assertTrue('does not support targeting' in ctx.exception.message)
|
||||
self.assertTrue('does not support targeting' in
|
||||
text_type(ctx.exception))
|
||||
|
||||
def test_always_dry_run(self):
|
||||
with TemporaryDirectory() as tmpdir:
|
||||
@@ -180,9 +183,9 @@ class TestManager(TestCase):
|
||||
'unit.tests.')
|
||||
self.assertEquals(14, len(changes))
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
manager.compare(['nope'], ['dump'], 'unit.tests.')
|
||||
self.assertEquals('Unknown source: nope', ctx.exception.message)
|
||||
self.assertEquals('Unknown source: nope', text_type(ctx.exception))
|
||||
|
||||
def test_aggregate_target(self):
|
||||
simple = SimpleProvider()
|
||||
@@ -220,10 +223,10 @@ class TestManager(TestCase):
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
manager = Manager(get_config_filename('simple.yaml'))
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, False,
|
||||
'nope')
|
||||
self.assertEquals('Unknown source: nope', ctx.exception.message)
|
||||
self.assertEquals('Unknown source: nope', text_type(ctx.exception))
|
||||
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, False, 'in')
|
||||
|
||||
@@ -249,10 +252,10 @@ class TestManager(TestCase):
|
||||
environ['YAML_TMP_DIR'] = tmpdir.dirname
|
||||
manager = Manager(get_config_filename('simple-split.yaml'))
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, True,
|
||||
'nope')
|
||||
self.assertEquals('Unknown source: nope', ctx.exception.message)
|
||||
self.assertEquals('Unknown source: nope', text_type(ctx.exception))
|
||||
|
||||
manager.dump('unit.tests.', tmpdir.dirname, False, True, 'in')
|
||||
|
||||
@@ -265,15 +268,15 @@ class TestManager(TestCase):
|
||||
def test_validate_configs(self):
|
||||
Manager(get_config_filename('simple-validate.yaml')).validate_configs()
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('missing-sources.yaml')) \
|
||||
.validate_configs()
|
||||
self.assertTrue('missing sources' in ctx.exception.message)
|
||||
self.assertTrue('missing sources' in text_type(ctx.exception))
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
with self.assertRaises(ManagerException) as ctx:
|
||||
Manager(get_config_filename('unknown-provider.yaml')) \
|
||||
.validate_configs()
|
||||
self.assertTrue('unknown source' in ctx.exception.message)
|
||||
self.assertTrue('unknown source' in text_type(ctx.exception))
|
||||
|
||||
|
||||
class TestMainThreadExecutor(TestCase):
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from StringIO import StringIO
|
||||
from logging import getLogger
|
||||
from six import StringIO, text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.provider.plan import Plan, PlanHtml, PlanLogger, PlanMarkdown
|
||||
@@ -59,7 +59,7 @@ class TestPlanLogger(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
PlanLogger('invalid', 'not-a-level')
|
||||
self.assertEquals('Unsupported level: not-a-level',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
def test_create(self):
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from logging import getLogger
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Create, Delete, Record, Update
|
||||
@@ -48,7 +49,7 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
BaseProvider('base')
|
||||
self.assertEquals('Abstract base class, log property missing',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
class HasLog(BaseProvider):
|
||||
log = getLogger('HasLog')
|
||||
@@ -56,7 +57,7 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasLog('haslog')
|
||||
self.assertEquals('Abstract base class, SUPPORTS_GEO property missing',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
class HasSupportsGeo(HasLog):
|
||||
SUPPORTS_GEO = False
|
||||
@@ -65,14 +66,14 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasSupportsGeo('hassupportsgeo').populate(zone)
|
||||
self.assertEquals('Abstract base class, SUPPORTS property missing',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
class HasSupports(HasSupportsGeo):
|
||||
SUPPORTS = set(('A',))
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasSupports('hassupports').populate(zone)
|
||||
self.assertEquals('Abstract base class, populate method missing',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
# SUPPORTS_DYNAMIC has a default/fallback
|
||||
self.assertFalse(HasSupports('hassupports').SUPPORTS_DYNAMIC)
|
||||
@@ -118,7 +119,7 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(NotImplementedError) as ctx:
|
||||
HasPopulate('haspopulate').apply(plan)
|
||||
self.assertEquals('Abstract base class, _apply method missing',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
def test_plan(self):
|
||||
ignored = Zone('unit.tests.', [])
|
||||
@@ -193,7 +194,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -225,7 +226,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -238,7 +239,7 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many updates' in ctx.exception.message)
|
||||
self.assertTrue('Too many updates' in text_type(ctx.exception))
|
||||
|
||||
def test_safe_updates_min_existing_pcent(self):
|
||||
# MAX_SAFE_UPDATE_PCENT is safe when more
|
||||
@@ -251,7 +252,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -273,7 +274,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -286,7 +287,7 @@ class TestBaseProvider(TestCase):
|
||||
with self.assertRaises(UnsafePlan) as ctx:
|
||||
Plan(zone, zone, changes, True).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many deletes' in ctx.exception.message)
|
||||
self.assertTrue('Too many deletes' in text_type(ctx.exception))
|
||||
|
||||
def test_safe_deletes_min_existing_pcent(self):
|
||||
# MAX_SAFE_DELETE_PCENT is safe when more
|
||||
@@ -299,7 +300,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -322,7 +323,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -336,7 +337,7 @@ class TestBaseProvider(TestCase):
|
||||
Plan(zone, zone, changes, True,
|
||||
update_pcent_threshold=safe_pcent).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many updates' in ctx.exception.message)
|
||||
self.assertTrue('Too many updates' in text_type(ctx.exception))
|
||||
|
||||
def test_safe_deletes_min_existing_override(self):
|
||||
safe_pcent = .4
|
||||
@@ -350,7 +351,7 @@ class TestBaseProvider(TestCase):
|
||||
})
|
||||
|
||||
for i in range(int(Plan.MIN_EXISTING_RECORDS)):
|
||||
zone.add_record(Record.new(zone, unicode(i), {
|
||||
zone.add_record(Record.new(zone, text_type(i), {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'value': '2.3.4.5'
|
||||
@@ -364,4 +365,4 @@ class TestBaseProvider(TestCase):
|
||||
Plan(zone, zone, changes, True,
|
||||
delete_pcent_threshold=safe_pcent).raise_if_unsafe()
|
||||
|
||||
self.assertTrue('Too many deletes' in ctx.exception.message)
|
||||
self.assertTrue('Too many deletes' in text_type(ctx.exception))
|
||||
|
||||
@@ -9,6 +9,7 @@ from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record, Update
|
||||
@@ -65,7 +66,7 @@ class TestCloudflareProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertEquals('CloudflareError', type(ctx.exception).__name__)
|
||||
self.assertEquals('request was invalid', ctx.exception.message)
|
||||
self.assertEquals('request was invalid', text_type(ctx.exception))
|
||||
|
||||
# Bad auth
|
||||
with requests_mock() as mock:
|
||||
@@ -80,7 +81,7 @@ class TestCloudflareProvider(TestCase):
|
||||
self.assertEquals('CloudflareAuthenticationError',
|
||||
type(ctx.exception).__name__)
|
||||
self.assertEquals('Unknown X-Auth-Key or X-Auth-Email',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
# Bad auth, unknown resp
|
||||
with requests_mock() as mock:
|
||||
@@ -91,7 +92,7 @@ class TestCloudflareProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals('CloudflareAuthenticationError',
|
||||
type(ctx.exception).__name__)
|
||||
self.assertEquals('Cloudflare error', ctx.exception.message)
|
||||
self.assertEquals('Cloudflare error', text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
@@ -102,7 +103,7 @@ class TestCloudflareProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=200, json=self.empty)
|
||||
|
||||
@@ -110,7 +111,7 @@ class TestCloudflareProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(set(), zone.records)
|
||||
|
||||
# re-populating the same non-existant zone uses cache and makes no
|
||||
# re-populating the same non-existent zone uses cache and makes no
|
||||
# calls
|
||||
again = Zone('unit.tests.', [])
|
||||
provider.populate(again)
|
||||
@@ -173,7 +174,7 @@ class TestCloudflareProvider(TestCase):
|
||||
}, # zone create
|
||||
] + [None] * 20 # individual record creates
|
||||
|
||||
# non-existant zone, create everything
|
||||
# non-existent zone, create everything
|
||||
plan = provider.plan(self.expected)
|
||||
self.assertEquals(12, len(plan.changes))
|
||||
self.assertEquals(12, provider.apply(plan))
|
||||
@@ -742,23 +743,25 @@ class TestCloudflareProvider(TestCase):
|
||||
# the CDN.
|
||||
self.assertEquals(3, len(zone.records))
|
||||
|
||||
record = list(zone.records)[0]
|
||||
self.assertEquals('multi', record.name)
|
||||
self.assertEquals('multi.unit.tests.', record.fqdn)
|
||||
self.assertEquals('CNAME', record._type)
|
||||
self.assertEquals('multi.unit.tests.cdn.cloudflare.net.', record.value)
|
||||
ordered = sorted(zone.records, key=lambda r: r.name)
|
||||
|
||||
record = list(zone.records)[1]
|
||||
record = ordered[0]
|
||||
self.assertEquals('a', record.name)
|
||||
self.assertEquals('a.unit.tests.', record.fqdn)
|
||||
self.assertEquals('CNAME', record._type)
|
||||
self.assertEquals('a.unit.tests.cdn.cloudflare.net.', record.value)
|
||||
|
||||
record = ordered[1]
|
||||
self.assertEquals('cname', record.name)
|
||||
self.assertEquals('cname.unit.tests.', record.fqdn)
|
||||
self.assertEquals('CNAME', record._type)
|
||||
self.assertEquals('cname.unit.tests.cdn.cloudflare.net.', record.value)
|
||||
|
||||
record = list(zone.records)[2]
|
||||
self.assertEquals('a', record.name)
|
||||
self.assertEquals('a.unit.tests.', record.fqdn)
|
||||
record = ordered[2]
|
||||
self.assertEquals('multi', record.name)
|
||||
self.assertEquals('multi.unit.tests.', record.fqdn)
|
||||
self.assertEquals('CNAME', record._type)
|
||||
self.assertEquals('a.unit.tests.cdn.cloudflare.net.', record.value)
|
||||
self.assertEquals('multi.unit.tests.cdn.cloudflare.net.', record.value)
|
||||
|
||||
# CDN enabled records can't be updated, we don't know the real values
|
||||
# never point a Cloudflare record to itself.
|
||||
@@ -950,7 +953,7 @@ class TestCloudflareProvider(TestCase):
|
||||
'value': 'ns1.unit.tests.'
|
||||
})
|
||||
|
||||
data = provider._gen_data(record).next()
|
||||
data = next(provider._gen_data(record))
|
||||
|
||||
self.assertFalse('proxied' in data)
|
||||
|
||||
@@ -965,7 +968,7 @@ class TestCloudflareProvider(TestCase):
|
||||
}), False
|
||||
)
|
||||
|
||||
data = provider._gen_data(record).next()
|
||||
data = next(provider._gen_data(record))
|
||||
|
||||
self.assertFalse(data['proxied'])
|
||||
|
||||
@@ -980,7 +983,7 @@ class TestCloudflareProvider(TestCase):
|
||||
}), True
|
||||
)
|
||||
|
||||
data = provider._gen_data(record).next()
|
||||
data = next(provider._gen_data(record))
|
||||
|
||||
self.assertTrue(data['proxied'])
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
from octodns.provider.constellix import ConstellixClientNotFound, \
|
||||
ConstellixProvider
|
||||
from octodns.provider.yaml import YamlProvider
|
||||
from octodns.zone import Zone
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class TestConstellixProvider(TestCase):
|
||||
expected = Zone('unit.tests.', [])
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
source.populate(expected)
|
||||
|
||||
# Our test suite differs a bit, add our NS and remove the simple one
|
||||
expected.add_record(Record.new(expected, 'under', {
|
||||
'ttl': 3600,
|
||||
'type': 'NS',
|
||||
'values': [
|
||||
'ns1.unit.tests.',
|
||||
'ns2.unit.tests.',
|
||||
]
|
||||
}))
|
||||
|
||||
# Add some ALIAS records
|
||||
expected.add_record(Record.new(expected, '', {
|
||||
'ttl': 1800,
|
||||
'type': 'ALIAS',
|
||||
'value': 'aname.unit.tests.'
|
||||
}))
|
||||
|
||||
expected.add_record(Record.new(expected, 'sub', {
|
||||
'ttl': 1800,
|
||||
'type': 'ALIAS',
|
||||
'value': 'aname.unit.tests.'
|
||||
}))
|
||||
|
||||
for record in list(expected.records):
|
||||
if record.name == 'sub' and record._type == 'NS':
|
||||
expected._remove_record(record)
|
||||
break
|
||||
|
||||
def test_populate(self):
|
||||
provider = ConstellixProvider('test', 'api', 'secret')
|
||||
|
||||
# Bad auth
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=401,
|
||||
text='{"errors": ["Unable to authenticate token"]}')
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('Unauthorized', text_type(ctx.exception))
|
||||
|
||||
# Bad request
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=400,
|
||||
text='{"errors": ["\\"unittests\\" is not '
|
||||
'a valid domain name"]}')
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('\n - "unittests" is not a valid domain name',
|
||||
text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=502, text='Things caught fire')
|
||||
|
||||
with self.assertRaises(HTTPError) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404,
|
||||
text='<html><head></head><body></body></html>')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(set(), zone.records)
|
||||
|
||||
# No diffs == no changes
|
||||
with requests_mock() as mock:
|
||||
base = 'https://api.dns.constellix.com/v1/domains'
|
||||
with open('tests/fixtures/constellix-domains.json') as fh:
|
||||
mock.get('{}{}'.format(base, '/'), text=fh.read())
|
||||
with open('tests/fixtures/constellix-records.json') as fh:
|
||||
mock.get('{}{}'.format(base, '/123123/records'),
|
||||
text=fh.read())
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(15, len(zone.records))
|
||||
changes = self.expected.changes(zone, provider)
|
||||
self.assertEquals(0, len(changes))
|
||||
|
||||
# 2nd populate makes no network calls/all from cache
|
||||
again = Zone('unit.tests.', [])
|
||||
provider.populate(again)
|
||||
self.assertEquals(15, len(again.records))
|
||||
|
||||
# bust the cache
|
||||
del provider._zone_records[zone.name]
|
||||
|
||||
def test_apply(self):
|
||||
provider = ConstellixProvider('test', 'api', 'secret')
|
||||
|
||||
resp = Mock()
|
||||
resp.json = Mock()
|
||||
provider._client._request = Mock(return_value=resp)
|
||||
|
||||
with open('tests/fixtures/constellix-domains.json') as fh:
|
||||
domains = json.load(fh)
|
||||
|
||||
# non-existent domain, create everything
|
||||
resp.json.side_effect = [
|
||||
ConstellixClientNotFound, # no zone in populate
|
||||
ConstellixClientNotFound, # no domain during apply
|
||||
domains
|
||||
]
|
||||
plan = provider.plan(self.expected)
|
||||
|
||||
# No root NS, no ignored, no excluded, no unsupported
|
||||
n = len(self.expected.records) - 5
|
||||
self.assertEquals(n, len(plan.changes))
|
||||
self.assertEquals(n, provider.apply(plan))
|
||||
|
||||
provider._client._request.assert_has_calls([
|
||||
# created the domain
|
||||
call('POST', '/', data={'names': ['unit.tests']}),
|
||||
# get all domains to build the cache
|
||||
call('GET', '/'),
|
||||
])
|
||||
# These two checks are broken up so that ordering doesn't break things.
|
||||
# Python3 doesn't make the calls in a consistent order so different
|
||||
# things follow the GET / on different runs
|
||||
provider._client._request.assert_has_calls([
|
||||
call('POST', '/123123/records/SRV', data={
|
||||
'roundRobin': [{
|
||||
'priority': 10,
|
||||
'weight': 20,
|
||||
'value': 'foo-1.unit.tests.',
|
||||
'port': 30
|
||||
}, {
|
||||
'priority': 12,
|
||||
'weight': 20,
|
||||
'value': 'foo-2.unit.tests.',
|
||||
'port': 30
|
||||
}],
|
||||
'name': '_srv._tcp',
|
||||
'ttl': 600,
|
||||
}),
|
||||
])
|
||||
|
||||
self.assertEquals(20, provider._client._request.call_count)
|
||||
|
||||
provider._client._request.reset_mock()
|
||||
|
||||
provider._client.records = Mock(return_value=[
|
||||
{
|
||||
'id': 11189897,
|
||||
'type': 'A',
|
||||
'name': 'www',
|
||||
'ttl': 300,
|
||||
'value': [
|
||||
'1.2.3.4',
|
||||
'2.2.3.4',
|
||||
]
|
||||
}, {
|
||||
'id': 11189898,
|
||||
'type': 'A',
|
||||
'name': 'ttl',
|
||||
'ttl': 600,
|
||||
'value': [
|
||||
'3.2.3.4'
|
||||
]
|
||||
}, {
|
||||
'id': 11189899,
|
||||
'type': 'ALIAS',
|
||||
'name': 'alias',
|
||||
'ttl': 600,
|
||||
'value': [{
|
||||
'value': 'aname.unit.tests.'
|
||||
}]
|
||||
}
|
||||
])
|
||||
|
||||
# Domain exists, we don't care about return
|
||||
resp.json.side_effect = ['{}']
|
||||
|
||||
wanted = Zone('unit.tests.', [])
|
||||
wanted.add_record(Record.new(wanted, 'ttl', {
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
'value': '3.2.3.4'
|
||||
}))
|
||||
|
||||
plan = provider.plan(wanted)
|
||||
self.assertEquals(3, len(plan.changes))
|
||||
self.assertEquals(3, provider.apply(plan))
|
||||
|
||||
# recreate for update, and deletes for the 2 parts of the other
|
||||
provider._client._request.assert_has_calls([
|
||||
call('POST', '/123123/records/A', data={
|
||||
'roundRobin': [{
|
||||
'value': '3.2.3.4'
|
||||
}],
|
||||
'name': 'ttl',
|
||||
'ttl': 300
|
||||
}),
|
||||
call('DELETE', '/123123/records/A/11189897'),
|
||||
call('DELETE', '/123123/records/A/11189898'),
|
||||
call('DELETE', '/123123/records/ANAME/11189899')
|
||||
], any_order=True)
|
||||
@@ -10,6 +10,7 @@ from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
@@ -50,7 +51,7 @@ class TestDigitalOceanProvider(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('Unauthorized', ctx.exception.message)
|
||||
self.assertEquals('Unauthorized', text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
@@ -61,7 +62,7 @@ class TestDigitalOceanProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404,
|
||||
text='{"id":"not_found","message":"The resource you '
|
||||
@@ -153,7 +154,7 @@ class TestDigitalOceanProvider(TestCase):
|
||||
}
|
||||
}
|
||||
|
||||
# non-existant domain, create everything
|
||||
# non-existent domain, create everything
|
||||
resp.json.side_effect = [
|
||||
DigitalOceanClientNotFound, # no zone in populate
|
||||
DigitalOceanClientNotFound, # no domain during apply
|
||||
@@ -175,7 +176,20 @@ class TestDigitalOceanProvider(TestCase):
|
||||
call('GET', '/domains/unit.tests/records', {'page': 1}),
|
||||
# delete the initial A record
|
||||
call('DELETE', '/domains/unit.tests/records/11189877'),
|
||||
# created at least one of the record with expected data
|
||||
# created at least some of the record with expected data
|
||||
call('POST', '/domains/unit.tests/records', data={
|
||||
'data': '1.2.3.4',
|
||||
'name': '@',
|
||||
'ttl': 300, 'type': 'A'}),
|
||||
call('POST', '/domains/unit.tests/records', data={
|
||||
'data': '1.2.3.5',
|
||||
'name': '@',
|
||||
'ttl': 300, 'type': 'A'}),
|
||||
call('POST', '/domains/unit.tests/records', data={
|
||||
'data': 'ca.unit.tests.',
|
||||
'flags': 0, 'name': '@',
|
||||
'tag': 'issue',
|
||||
'ttl': 3600, 'type': 'CAA'}),
|
||||
call('POST', '/domains/unit.tests/records', data={
|
||||
'name': '_srv._tcp',
|
||||
'weight': 20,
|
||||
|
||||
@@ -9,6 +9,7 @@ from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
@@ -47,7 +48,7 @@ class TestDnsimpleProvider(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('Unauthorized', ctx.exception.message)
|
||||
self.assertEquals('Unauthorized', text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
@@ -58,7 +59,7 @@ class TestDnsimpleProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404,
|
||||
text='{"message": "Domain `foo.bar` not found"}')
|
||||
@@ -122,7 +123,7 @@ class TestDnsimpleProvider(TestCase):
|
||||
resp.json = Mock()
|
||||
provider._client._request = Mock(return_value=resp)
|
||||
|
||||
# non-existant domain, create everything
|
||||
# non-existent domain, create everything
|
||||
resp.json.side_effect = [
|
||||
DnsimpleClientNotFound, # no zone in populate
|
||||
DnsimpleClientNotFound, # no domain during apply
|
||||
@@ -138,7 +139,32 @@ class TestDnsimpleProvider(TestCase):
|
||||
provider._client._request.assert_has_calls([
|
||||
# created the domain
|
||||
call('POST', '/domains', data={'name': 'unit.tests'}),
|
||||
# created at least one of the record with expected data
|
||||
# created at least some of the record with expected data
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '1.2.3.4',
|
||||
'type': 'A',
|
||||
'name': '',
|
||||
'ttl': 300}),
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '1.2.3.5',
|
||||
'type': 'A',
|
||||
'name': '',
|
||||
'ttl': 300}),
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '0 issue "ca.unit.tests"',
|
||||
'type': 'CAA',
|
||||
'name': '',
|
||||
'ttl': 3600}),
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49',
|
||||
'type': 'SSHFP',
|
||||
'name': '',
|
||||
'ttl': 3600}),
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73',
|
||||
'type': 'SSHFP',
|
||||
'name': '',
|
||||
'ttl': 3600}),
|
||||
call('POST', '/zones/unit.tests/records', data={
|
||||
'content': '20 30 foo-1.unit.tests.',
|
||||
'priority': 10,
|
||||
|
||||
@@ -10,6 +10,7 @@ from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
@@ -65,7 +66,7 @@ class TestDnsMadeEasyProvider(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('Unauthorized', ctx.exception.message)
|
||||
self.assertEquals('Unauthorized', text_type(ctx.exception))
|
||||
|
||||
# Bad request
|
||||
with requests_mock() as mock:
|
||||
@@ -76,7 +77,7 @@ class TestDnsMadeEasyProvider(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals('\n - Rate limit exceeded',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
@@ -87,7 +88,7 @@ class TestDnsMadeEasyProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404,
|
||||
text='<html><head></head><body></body></html>')
|
||||
@@ -130,7 +131,7 @@ class TestDnsMadeEasyProvider(TestCase):
|
||||
with open('tests/fixtures/dnsmadeeasy-domains.json') as fh:
|
||||
domains = json.load(fh)
|
||||
|
||||
# non-existant domain, create everything
|
||||
# non-existent domain, create everything
|
||||
resp.json.side_effect = [
|
||||
DnsMadeEasyClientNotFound, # no zone in populate
|
||||
DnsMadeEasyClientNotFound, # no domain during apply
|
||||
@@ -148,7 +149,27 @@ class TestDnsMadeEasyProvider(TestCase):
|
||||
call('POST', '/', data={'name': 'unit.tests'}),
|
||||
# get all domains to build the cache
|
||||
call('GET', '/'),
|
||||
# created at least one of the record with expected data
|
||||
# created at least some of the record with expected data
|
||||
call('POST', '/123123/records', data={
|
||||
'type': 'A',
|
||||
'name': '',
|
||||
'value': '1.2.3.4',
|
||||
'ttl': 300}),
|
||||
call('POST', '/123123/records', data={
|
||||
'type': 'A',
|
||||
'name': '',
|
||||
'value': '1.2.3.5',
|
||||
'ttl': 300}),
|
||||
call('POST', '/123123/records', data={
|
||||
'type': 'ANAME',
|
||||
'name': '',
|
||||
'value': 'aname.unit.tests.',
|
||||
'ttl': 1800}),
|
||||
call('POST', '/123123/records', data={
|
||||
'name': '',
|
||||
'value': 'ca.unit.tests',
|
||||
'issuerCritical': 0, 'caaType': 'issue',
|
||||
'ttl': 3600, 'type': 'CAA'}),
|
||||
call('POST', '/123123/records', data={
|
||||
'name': '_srv._tcp',
|
||||
'weight': 20,
|
||||
|
||||
@@ -670,8 +670,8 @@ class TestDynProviderGeo(TestCase):
|
||||
tds = provider.traffic_directors
|
||||
self.assertEquals(set(['unit.tests.', 'geo.unit.tests.']),
|
||||
set(tds.keys()))
|
||||
self.assertEquals(['A'], tds['unit.tests.'].keys())
|
||||
self.assertEquals(['A'], tds['geo.unit.tests.'].keys())
|
||||
self.assertEquals(['A'], list(tds['unit.tests.'].keys()))
|
||||
self.assertEquals(['A'], list(tds['geo.unit.tests.'].keys()))
|
||||
provider.log.warn.assert_called_with("Unsupported TrafficDirector "
|
||||
"'%s'", 'something else')
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
# from mock import Mock, call
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
from octodns.provider.fastdns import AkamaiProvider
|
||||
from octodns.provider.yaml import YamlProvider
|
||||
from octodns.zone import Zone
|
||||
|
||||
|
||||
class TestFastdnsProvider(TestCase):
|
||||
expected = Zone('unit.tests.', [])
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
source.populate(expected)
|
||||
|
||||
# Our test suite differs a bit, add our NS and remove the simple one
|
||||
expected.add_record(Record.new(expected, 'under', {
|
||||
'ttl': 3600,
|
||||
'type': 'NS',
|
||||
'values': [
|
||||
'ns1.unit.tests.',
|
||||
'ns2.unit.tests.',
|
||||
]
|
||||
}))
|
||||
for record in list(expected.records):
|
||||
if record.name == 'sub' and record._type == 'NS':
|
||||
expected._remove_record(record)
|
||||
break
|
||||
|
||||
def test_populate(self):
|
||||
provider = AkamaiProvider("test", "secret", "akam.com", "atok", "ctok")
|
||||
|
||||
# Bad Auth
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=401, text='{"message": "Unauthorized"}')
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertEquals(401, ctx.exception.response.status_code)
|
||||
|
||||
# general error
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=502, text='Things caught fire')
|
||||
|
||||
with self.assertRaises(HTTPError) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404,
|
||||
text='{"message": "Domain `foo.bar` not found"}')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(set(), zone.records)
|
||||
|
||||
# No diffs == no changes
|
||||
with requests_mock() as mock:
|
||||
|
||||
with open('tests/fixtures/fastdns-records.json') as fh:
|
||||
mock.get(ANY, text=fh.read())
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(16, len(zone.records))
|
||||
changes = self.expected.changes(zone, provider)
|
||||
self.assertEquals(0, len(changes))
|
||||
|
||||
# 2nd populate makes no network calls/all from cache
|
||||
again = Zone('unit.tests.', [])
|
||||
provider.populate(again)
|
||||
self.assertEquals(16, len(again.records))
|
||||
|
||||
# bust the cache
|
||||
del provider._zone_records[zone.name]
|
||||
|
||||
def test_apply(self):
|
||||
provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok",
|
||||
"cid", "gid")
|
||||
|
||||
# tests create update delete through previous state config json
|
||||
with requests_mock() as mock:
|
||||
|
||||
with open('tests/fixtures/fastdns-records-prev.json') as fh:
|
||||
mock.get(ANY, text=fh.read())
|
||||
|
||||
plan = provider.plan(self.expected)
|
||||
mock.post(ANY, status_code=201)
|
||||
mock.put(ANY, status_code=200)
|
||||
mock.delete(ANY, status_code=204)
|
||||
|
||||
changes = provider.apply(plan)
|
||||
self.assertEquals(29, changes)
|
||||
|
||||
# Test against a zone that doesn't exist yet
|
||||
with requests_mock() as mock:
|
||||
with open('tests/fixtures/fastdns-records-prev-other.json') as fh:
|
||||
mock.get(ANY, status_code=404)
|
||||
|
||||
plan = provider.plan(self.expected)
|
||||
mock.post(ANY, status_code=201)
|
||||
mock.put(ANY, status_code=200)
|
||||
mock.delete(ANY, status_code=204)
|
||||
|
||||
changes = provider.apply(plan)
|
||||
self.assertEquals(14, changes)
|
||||
|
||||
# Test against a zone that doesn't exist yet, but gid not provided
|
||||
with requests_mock() as mock:
|
||||
with open('tests/fixtures/fastdns-records-prev-other.json') as fh:
|
||||
mock.get(ANY, status_code=404)
|
||||
provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok",
|
||||
"cid")
|
||||
plan = provider.plan(self.expected)
|
||||
mock.post(ANY, status_code=201)
|
||||
mock.put(ANY, status_code=200)
|
||||
mock.delete(ANY, status_code=204)
|
||||
|
||||
changes = provider.apply(plan)
|
||||
self.assertEquals(14, changes)
|
||||
|
||||
# Test against a zone that doesn't exist, but cid not provided
|
||||
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=404)
|
||||
|
||||
provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok")
|
||||
plan = provider.plan(self.expected)
|
||||
mock.post(ANY, status_code=201)
|
||||
mock.put(ANY, status_code=200)
|
||||
mock.delete(ANY, status_code=204)
|
||||
|
||||
try:
|
||||
changes = provider.apply(plan)
|
||||
except NameError as e:
|
||||
expected = "contractId not specified to create zone"
|
||||
self.assertEquals(text_type(e), expected)
|
||||
@@ -193,8 +193,13 @@ class DummyIterator:
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
# python2
|
||||
def next(self):
|
||||
return self.iterable.next()
|
||||
return next(self.iterable)
|
||||
|
||||
# python3
|
||||
def __next__(self):
|
||||
return next(self.iterable)
|
||||
|
||||
|
||||
class TestGoogleCloudProvider(TestCase):
|
||||
@@ -247,7 +252,7 @@ class TestGoogleCloudProvider(TestCase):
|
||||
return_values_for_status = iter(
|
||||
["pending"] * 11 + ['done', 'done'])
|
||||
type(status_mock).status = PropertyMock(
|
||||
side_effect=return_values_for_status.next)
|
||||
side_effect=lambda: next(return_values_for_status))
|
||||
gcloud_zone_mock.changes = Mock(return_value=status_mock)
|
||||
|
||||
provider = self._get_provider()
|
||||
|
||||
@@ -0,0 +1,451 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from os.path import dirname, join
|
||||
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.provider.mythicbeasts import MythicBeastsProvider, \
|
||||
add_trailing_dot, remove_trailing_dot
|
||||
from octodns.provider.yaml import YamlProvider
|
||||
from octodns.zone import Zone
|
||||
from octodns.record import Create, Update, Delete, Record
|
||||
|
||||
|
||||
class TestMythicBeastsProvider(TestCase):
|
||||
expected = Zone('unit.tests.', [])
|
||||
source = YamlProvider('test_expected', join(dirname(__file__), 'config'))
|
||||
source.populate(expected)
|
||||
|
||||
# Dump anything we don't support from expected
|
||||
for record in list(expected.records):
|
||||
if record._type not in MythicBeastsProvider.SUPPORTS:
|
||||
expected._remove_record(record)
|
||||
|
||||
def test_trailing_dot(self):
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
add_trailing_dot('unit.tests.')
|
||||
self.assertEquals('Value already has trailing dot',
|
||||
text_type(err.exception))
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
remove_trailing_dot('unit.tests')
|
||||
self.assertEquals('Value already missing trailing dot',
|
||||
text_type(err.exception))
|
||||
|
||||
self.assertEquals(add_trailing_dot('unit.tests'), 'unit.tests.')
|
||||
self.assertEquals(remove_trailing_dot('unit.tests.'), 'unit.tests')
|
||||
|
||||
def test_data_for_single(self):
|
||||
test_data = {
|
||||
'raw_values': [{'value': 'a:a::c', 'ttl': 0}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_single = MythicBeastsProvider._data_for_single('', test_data)
|
||||
self.assertTrue(isinstance(test_single, dict))
|
||||
self.assertEquals('a:a::c', test_single['value'])
|
||||
|
||||
def test_data_for_multiple(self):
|
||||
test_data = {
|
||||
'raw_values': [
|
||||
{'value': 'b:b::d', 'ttl': 60},
|
||||
{'value': 'a:a::c', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_multiple = MythicBeastsProvider._data_for_multiple('', test_data)
|
||||
self.assertTrue(isinstance(test_multiple, dict))
|
||||
self.assertEquals(2, len(test_multiple['values']))
|
||||
|
||||
def test_data_for_txt(self):
|
||||
test_data = {
|
||||
'raw_values': [
|
||||
{'value': 'v=DKIM1; k=rsa; p=prawf', 'ttl': 60},
|
||||
{'value': 'prawf prawf dyma prawf', 'ttl': 300}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_txt = MythicBeastsProvider._data_for_TXT('', test_data)
|
||||
self.assertTrue(isinstance(test_txt, dict))
|
||||
self.assertEquals(2, len(test_txt['values']))
|
||||
self.assertEquals('v=DKIM1\\; k=rsa\\; p=prawf', test_txt['values'][0])
|
||||
|
||||
def test_data_for_MX(self):
|
||||
test_data = {
|
||||
'raw_values': [
|
||||
{'value': '10 un.unit', 'ttl': 60},
|
||||
{'value': '20 dau.unit', 'ttl': 60},
|
||||
{'value': '30 tri.unit', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_MX = MythicBeastsProvider._data_for_MX('', test_data)
|
||||
self.assertTrue(isinstance(test_MX, dict))
|
||||
self.assertEquals(3, len(test_MX['values']))
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
test_MX = MythicBeastsProvider._data_for_MX(
|
||||
'',
|
||||
{'raw_values': [{'value': '', 'ttl': 0}]}
|
||||
)
|
||||
self.assertEquals('Unable to parse MX data',
|
||||
text_type(err.exception))
|
||||
|
||||
def test_data_for_CNAME(self):
|
||||
test_data = {
|
||||
'raw_values': [{'value': 'cname', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_cname = MythicBeastsProvider._data_for_CNAME('', test_data)
|
||||
self.assertTrue(isinstance(test_cname, dict))
|
||||
self.assertEquals('cname.unit.tests.', test_cname['value'])
|
||||
|
||||
def test_data_for_ANAME(self):
|
||||
test_data = {
|
||||
'raw_values': [{'value': 'aname', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_aname = MythicBeastsProvider._data_for_ANAME('', test_data)
|
||||
self.assertTrue(isinstance(test_aname, dict))
|
||||
self.assertEquals('aname', test_aname['value'])
|
||||
|
||||
def test_data_for_SRV(self):
|
||||
test_data = {
|
||||
'raw_values': [
|
||||
{'value': '10 20 30 un.srv.unit', 'ttl': 60},
|
||||
{'value': '20 30 40 dau.srv.unit', 'ttl': 60},
|
||||
{'value': '30 30 50 tri.srv.unit', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_SRV = MythicBeastsProvider._data_for_SRV('', test_data)
|
||||
self.assertTrue(isinstance(test_SRV, dict))
|
||||
self.assertEquals(3, len(test_SRV['values']))
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
test_SRV = MythicBeastsProvider._data_for_SRV(
|
||||
'',
|
||||
{'raw_values': [{'value': '', 'ttl': 0}]}
|
||||
)
|
||||
self.assertEquals('Unable to parse SRV data',
|
||||
text_type(err.exception))
|
||||
|
||||
def test_data_for_SSHFP(self):
|
||||
test_data = {
|
||||
'raw_values': [
|
||||
{'value': '1 1 0123456789abcdef', 'ttl': 60},
|
||||
{'value': '1 2 0123456789abcdef', 'ttl': 60},
|
||||
{'value': '2 3 0123456789abcdef', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_SSHFP = MythicBeastsProvider._data_for_SSHFP('', test_data)
|
||||
self.assertTrue(isinstance(test_SSHFP, dict))
|
||||
self.assertEquals(3, len(test_SSHFP['values']))
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
test_SSHFP = MythicBeastsProvider._data_for_SSHFP(
|
||||
'',
|
||||
{'raw_values': [{'value': '', 'ttl': 0}]}
|
||||
)
|
||||
self.assertEquals('Unable to parse SSHFP data',
|
||||
text_type(err.exception))
|
||||
|
||||
def test_data_for_CAA(self):
|
||||
test_data = {
|
||||
'raw_values': [{'value': '1 issue letsencrypt.org', 'ttl': 60}],
|
||||
'zone': 'unit.tests.',
|
||||
}
|
||||
test_CAA = MythicBeastsProvider._data_for_CAA('', test_data)
|
||||
self.assertTrue(isinstance(test_CAA, dict))
|
||||
self.assertEquals(3, len(test_CAA['value']))
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
test_CAA = MythicBeastsProvider._data_for_CAA(
|
||||
'',
|
||||
{'raw_values': [{'value': '', 'ttl': 0}]}
|
||||
)
|
||||
self.assertEquals('Unable to parse CAA data',
|
||||
text_type(err.exception))
|
||||
|
||||
def test_command_generation(self):
|
||||
zone = Zone('unit.tests.', [])
|
||||
zone.add_record(Record.new(zone, 'prawf-alias', {
|
||||
'ttl': 60,
|
||||
'type': 'ALIAS',
|
||||
'value': 'alias.unit.tests.',
|
||||
}))
|
||||
zone.add_record(Record.new(zone, 'prawf-ns', {
|
||||
'ttl': 300,
|
||||
'type': 'NS',
|
||||
'values': [
|
||||
'alias.unit.tests.',
|
||||
'alias2.unit.tests.',
|
||||
],
|
||||
}))
|
||||
zone.add_record(Record.new(zone, 'prawf-a', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': [
|
||||
'1.2.3.4',
|
||||
'5.6.7.8',
|
||||
],
|
||||
}))
|
||||
zone.add_record(Record.new(zone, 'prawf-aaaa', {
|
||||
'ttl': 60,
|
||||
'type': 'AAAA',
|
||||
'values': [
|
||||
'a:a::a',
|
||||
'b:b::b',
|
||||
'c:c::c:c',
|
||||
],
|
||||
}))
|
||||
zone.add_record(Record.new(zone, 'prawf-txt', {
|
||||
'ttl': 60,
|
||||
'type': 'TXT',
|
||||
'value': 'prawf prawf dyma prawf',
|
||||
}))
|
||||
zone.add_record(Record.new(zone, 'prawf-txt2', {
|
||||
'ttl': 60,
|
||||
'type': 'TXT',
|
||||
'value': 'v=DKIM1\\; k=rsa\\; p=prawf',
|
||||
}))
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text='')
|
||||
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
|
||||
plan = provider.plan(zone)
|
||||
changes = plan.changes
|
||||
generated_commands = []
|
||||
|
||||
for change in changes:
|
||||
generated_commands.extend(
|
||||
provider._compile_commands('ADD', change.new)
|
||||
)
|
||||
|
||||
expected_commands = [
|
||||
'ADD prawf-alias.unit.tests 60 ANAME alias.unit.tests.',
|
||||
'ADD prawf-ns.unit.tests 300 NS alias.unit.tests.',
|
||||
'ADD prawf-ns.unit.tests 300 NS alias2.unit.tests.',
|
||||
'ADD prawf-a.unit.tests 60 A 1.2.3.4',
|
||||
'ADD prawf-a.unit.tests 60 A 5.6.7.8',
|
||||
'ADD prawf-aaaa.unit.tests 60 AAAA a:a::a',
|
||||
'ADD prawf-aaaa.unit.tests 60 AAAA b:b::b',
|
||||
'ADD prawf-aaaa.unit.tests 60 AAAA c:c::c:c',
|
||||
'ADD prawf-txt.unit.tests 60 TXT prawf prawf dyma prawf',
|
||||
'ADD prawf-txt2.unit.tests 60 TXT v=DKIM1; k=rsa; p=prawf',
|
||||
]
|
||||
|
||||
generated_commands.sort()
|
||||
expected_commands.sort()
|
||||
|
||||
self.assertEquals(
|
||||
generated_commands,
|
||||
expected_commands
|
||||
)
|
||||
|
||||
# Now test deletion
|
||||
existing = 'prawf-txt 300 TXT prawf prawf dyma prawf\n' \
|
||||
'prawf-txt2 300 TXT v=DKIM1; k=rsa; p=prawf\n' \
|
||||
'prawf-a 60 A 1.2.3.4'
|
||||
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text=existing)
|
||||
wanted = Zone('unit.tests.', [])
|
||||
|
||||
plan = provider.plan(wanted)
|
||||
changes = plan.changes
|
||||
generated_commands = []
|
||||
|
||||
for change in changes:
|
||||
generated_commands.extend(
|
||||
provider._compile_commands('DELETE', change.existing)
|
||||
)
|
||||
|
||||
expected_commands = [
|
||||
'DELETE prawf-a.unit.tests 60 A 1.2.3.4',
|
||||
'DELETE prawf-txt.unit.tests 300 TXT prawf prawf dyma prawf',
|
||||
'DELETE prawf-txt2.unit.tests 300 TXT v=DKIM1; k=rsa; p=prawf',
|
||||
]
|
||||
|
||||
generated_commands.sort()
|
||||
expected_commands.sort()
|
||||
|
||||
self.assertEquals(
|
||||
generated_commands,
|
||||
expected_commands
|
||||
)
|
||||
|
||||
def test_fake_command_generation(self):
|
||||
class FakeChangeRecord(object):
|
||||
def __init__(self):
|
||||
self.__fqdn = 'prawf.unit.tests.'
|
||||
self._type = 'NOOP'
|
||||
self.value = 'prawf'
|
||||
self.ttl = 60
|
||||
|
||||
@property
|
||||
def record(self):
|
||||
return self
|
||||
|
||||
@property
|
||||
def fqdn(self):
|
||||
return self.__fqdn
|
||||
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text='')
|
||||
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
record = FakeChangeRecord()
|
||||
command = provider._compile_commands('ADD', record)
|
||||
self.assertEquals([], command)
|
||||
|
||||
def test_populate(self):
|
||||
provider = None
|
||||
|
||||
# Null passwords dict
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
provider = MythicBeastsProvider('test', None)
|
||||
self.assertEquals('Passwords must be a dictionary',
|
||||
text_type(err.exception))
|
||||
|
||||
# Missing password
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=401, text='ERR Not authenticated')
|
||||
|
||||
with self.assertRaises(AssertionError) as err:
|
||||
provider = MythicBeastsProvider('test', dict())
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(
|
||||
'Missing password for domain: unit.tests',
|
||||
text_type(err.exception))
|
||||
|
||||
# Failed authentication
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=401, text='ERR Not authenticated')
|
||||
|
||||
with self.assertRaises(Exception) as err:
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(
|
||||
'Mythic Beasts unauthorized for zone: unit.tests',
|
||||
err.exception.message)
|
||||
|
||||
# Check unmatched lines are ignored
|
||||
test_data = 'This should not match'
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text=test_data)
|
||||
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(0, len(zone.records))
|
||||
|
||||
# Check unsupported records are skipped
|
||||
test_data = '@ 60 NOOP prawf\n@ 60 SPF prawf prawf prawf'
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text=test_data)
|
||||
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(0, len(zone.records))
|
||||
|
||||
# Check no changes between what we support and what's parsed
|
||||
# from the unit.tests. config YAML. Also make sure we see the same
|
||||
# for both after we've thrown away records we don't support
|
||||
with requests_mock() as mock:
|
||||
with open('tests/fixtures/mythicbeasts-list.txt') as file_handle:
|
||||
mock.post(ANY, status_code=200, text=file_handle.read())
|
||||
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertEquals(15, len(zone.records))
|
||||
self.assertEquals(15, len(self.expected.records))
|
||||
changes = self.expected.changes(zone, provider)
|
||||
self.assertEquals(0, len(changes))
|
||||
|
||||
def test_apply(self):
|
||||
provider = MythicBeastsProvider('test', {
|
||||
'unit.tests.': 'mypassword'
|
||||
})
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
# Create blank zone
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text='')
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertEquals(0, len(zone.records))
|
||||
|
||||
# Record change failed
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text='')
|
||||
provider.populate(zone)
|
||||
zone.add_record(Record.new(zone, 'prawf', {
|
||||
'ttl': 300,
|
||||
'type': 'TXT',
|
||||
'value': 'prawf',
|
||||
}))
|
||||
plan = provider.plan(zone)
|
||||
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=400, text='NADD 300 TXT prawf')
|
||||
|
||||
with self.assertRaises(Exception) as err:
|
||||
provider.apply(plan)
|
||||
self.assertEquals(
|
||||
'Mythic Beasts could not action command: unit.tests '
|
||||
'ADD prawf.unit.tests 300 TXT prawf', err.exception.message)
|
||||
|
||||
# Check deleting and adding/changing test record
|
||||
existing = 'prawf 300 TXT prawf prawf prawf\ndileu 300 TXT dileu'
|
||||
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text=existing)
|
||||
|
||||
# Mash up a new zone with records so a plan
|
||||
# is generated with changes and applied. For some reason
|
||||
# passing self.expected, or just changing each record's zone
|
||||
# doesn't work. Nor does this without a single add_record after
|
||||
wanted = Zone('unit.tests.', [])
|
||||
for record in list(self.expected.records):
|
||||
data = {'type': record._type}
|
||||
data.update(record.data)
|
||||
wanted.add_record(Record.new(wanted, record.name, data))
|
||||
|
||||
wanted.add_record(Record.new(wanted, 'prawf', {
|
||||
'ttl': 60,
|
||||
'type': 'TXT',
|
||||
'value': 'prawf yw e',
|
||||
}))
|
||||
|
||||
plan = provider.plan(wanted)
|
||||
|
||||
# Octo ignores NS records (15-1)
|
||||
self.assertEquals(1, len([c for c in plan.changes
|
||||
if isinstance(c, Update)]))
|
||||
self.assertEquals(1, len([c for c in plan.changes
|
||||
if isinstance(c, Delete)]))
|
||||
self.assertEquals(14, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertEquals(16, provider.apply(plan))
|
||||
self.assertTrue(plan.exists)
|
||||
+202
-150
@@ -5,24 +5,18 @@
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from mock import Mock, call, patch
|
||||
from nsone.rest.errors import AuthException, RateLimitException, \
|
||||
from collections import defaultdict
|
||||
from mock import call, patch
|
||||
from ns1.rest.errors import AuthException, RateLimitException, \
|
||||
ResourceException
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Delete, Record, Update
|
||||
from octodns.provider.ns1 import Ns1Provider
|
||||
from octodns.provider.ns1 import Ns1Client, Ns1Provider
|
||||
from octodns.zone import Zone
|
||||
|
||||
|
||||
class DummyZone(object):
|
||||
|
||||
def __init__(self, records):
|
||||
self.data = {
|
||||
'records': records
|
||||
}
|
||||
|
||||
|
||||
class TestNs1Provider(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
expected = set()
|
||||
@@ -115,7 +109,7 @@ class TestNs1Provider(TestCase):
|
||||
},
|
||||
}))
|
||||
|
||||
nsone_records = [{
|
||||
ns1_records = [{
|
||||
'type': 'A',
|
||||
'ttl': 32,
|
||||
'short_answers': ['1.2.3.4'],
|
||||
@@ -171,43 +165,40 @@ class TestNs1Provider(TestCase):
|
||||
'domain': 'unit.tests.',
|
||||
}]
|
||||
|
||||
@patch('nsone.NSONE.loadZone')
|
||||
def test_populate(self, load_mock):
|
||||
@patch('ns1.rest.zones.Zones.retrieve')
|
||||
def test_populate(self, zone_retrieve_mock):
|
||||
provider = Ns1Provider('test', 'api-key')
|
||||
|
||||
# Bad auth
|
||||
load_mock.side_effect = AuthException('unauthorized')
|
||||
zone_retrieve_mock.side_effect = AuthException('unauthorized')
|
||||
zone = Zone('unit.tests.', [])
|
||||
with self.assertRaises(AuthException) as ctx:
|
||||
provider.populate(zone)
|
||||
self.assertEquals(load_mock.side_effect, ctx.exception)
|
||||
self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)
|
||||
|
||||
# General error
|
||||
load_mock.reset_mock()
|
||||
load_mock.side_effect = ResourceException('boom')
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = ResourceException('boom')
|
||||
zone = Zone('unit.tests.', [])
|
||||
with self.assertRaises(ResourceException) as ctx:
|
||||
provider.populate(zone)
|
||||
self.assertEquals(load_mock.side_effect, ctx.exception)
|
||||
self.assertEquals(('unit.tests',), load_mock.call_args[0])
|
||||
self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)
|
||||
self.assertEquals(('unit.tests',), zone_retrieve_mock.call_args[0])
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
load_mock.reset_mock()
|
||||
load_mock.side_effect = \
|
||||
# Non-existent zone doesn't populate anything
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = \
|
||||
ResourceException('server error: zone not found')
|
||||
zone = Zone('unit.tests.', [])
|
||||
exists = provider.populate(zone)
|
||||
self.assertEquals(set(), zone.records)
|
||||
self.assertEquals(('unit.tests',), load_mock.call_args[0])
|
||||
self.assertEquals(('unit.tests',), zone_retrieve_mock.call_args[0])
|
||||
self.assertFalse(exists)
|
||||
|
||||
# Existing zone w/o records
|
||||
load_mock.reset_mock()
|
||||
nsone_zone = DummyZone([])
|
||||
load_mock.side_effect = [nsone_zone]
|
||||
zone_search = Mock()
|
||||
zone_search.return_value = [
|
||||
{
|
||||
zone_retrieve_mock.reset_mock()
|
||||
ns1_zone = {
|
||||
'records': [{
|
||||
"domain": "geo.unit.tests",
|
||||
"zone": "unit.tests",
|
||||
"type": "A",
|
||||
@@ -221,21 +212,18 @@ class TestNs1Provider(TestCase):
|
||||
'meta': {'iso_region_code': ['NA-US-WA']}},
|
||||
],
|
||||
'ttl': 34,
|
||||
},
|
||||
]
|
||||
nsone_zone.search = zone_search
|
||||
}],
|
||||
}
|
||||
zone_retrieve_mock.side_effect = [ns1_zone]
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(1, len(zone.records))
|
||||
self.assertEquals(('unit.tests',), load_mock.call_args[0])
|
||||
self.assertEquals(('unit.tests',), zone_retrieve_mock.call_args[0])
|
||||
|
||||
# Existing zone w/records
|
||||
load_mock.reset_mock()
|
||||
nsone_zone = DummyZone(self.nsone_records)
|
||||
load_mock.side_effect = [nsone_zone]
|
||||
zone_search = Mock()
|
||||
zone_search.return_value = [
|
||||
{
|
||||
zone_retrieve_mock.reset_mock()
|
||||
ns1_zone = {
|
||||
'records': self.ns1_records + [{
|
||||
"domain": "geo.unit.tests",
|
||||
"zone": "unit.tests",
|
||||
"type": "A",
|
||||
@@ -249,26 +237,23 @@ class TestNs1Provider(TestCase):
|
||||
'meta': {'iso_region_code': ['NA-US-WA']}},
|
||||
],
|
||||
'ttl': 34,
|
||||
},
|
||||
]
|
||||
nsone_zone.search = zone_search
|
||||
}],
|
||||
}
|
||||
zone_retrieve_mock.side_effect = [ns1_zone]
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(self.expected, zone.records)
|
||||
self.assertEquals(('unit.tests',), load_mock.call_args[0])
|
||||
self.assertEquals(('unit.tests',), zone_retrieve_mock.call_args[0])
|
||||
|
||||
# Test skipping unsupported record type
|
||||
load_mock.reset_mock()
|
||||
nsone_zone = DummyZone(self.nsone_records + [{
|
||||
'type': 'UNSUPPORTED',
|
||||
'ttl': 42,
|
||||
'short_answers': ['unsupported'],
|
||||
'domain': 'unsupported.unit.tests.',
|
||||
}])
|
||||
load_mock.side_effect = [nsone_zone]
|
||||
zone_search = Mock()
|
||||
zone_search.return_value = [
|
||||
{
|
||||
zone_retrieve_mock.reset_mock()
|
||||
ns1_zone = {
|
||||
'records': self.ns1_records + [{
|
||||
'type': 'UNSUPPORTED',
|
||||
'ttl': 42,
|
||||
'short_answers': ['unsupported'],
|
||||
'domain': 'unsupported.unit.tests.',
|
||||
}, {
|
||||
"domain": "geo.unit.tests",
|
||||
"zone": "unit.tests",
|
||||
"type": "A",
|
||||
@@ -282,17 +267,23 @@ class TestNs1Provider(TestCase):
|
||||
'meta': {'iso_region_code': ['NA-US-WA']}},
|
||||
],
|
||||
'ttl': 34,
|
||||
},
|
||||
]
|
||||
nsone_zone.search = zone_search
|
||||
}],
|
||||
}
|
||||
zone_retrieve_mock.side_effect = [ns1_zone]
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertEquals(self.expected, zone.records)
|
||||
self.assertEquals(('unit.tests',), load_mock.call_args[0])
|
||||
self.assertEquals(('unit.tests',), zone_retrieve_mock.call_args[0])
|
||||
|
||||
@patch('nsone.NSONE.createZone')
|
||||
@patch('nsone.NSONE.loadZone')
|
||||
def test_sync(self, load_mock, create_mock):
|
||||
@patch('ns1.rest.records.Records.delete')
|
||||
@patch('ns1.rest.records.Records.update')
|
||||
@patch('ns1.rest.records.Records.create')
|
||||
@patch('ns1.rest.records.Records.retrieve')
|
||||
@patch('ns1.rest.zones.Zones.create')
|
||||
@patch('ns1.rest.zones.Zones.retrieve')
|
||||
def test_sync(self, zone_retrieve_mock, zone_create_mock,
|
||||
record_retrieve_mock, record_create_mock,
|
||||
record_update_mock, record_delete_mock):
|
||||
provider = Ns1Provider('test', 'api-key')
|
||||
|
||||
desired = Zone('unit.tests.', [])
|
||||
@@ -306,124 +297,142 @@ class TestNs1Provider(TestCase):
|
||||
self.assertTrue(plan.exists)
|
||||
|
||||
# Fails, general error
|
||||
load_mock.reset_mock()
|
||||
create_mock.reset_mock()
|
||||
load_mock.side_effect = ResourceException('boom')
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_create_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = ResourceException('boom')
|
||||
with self.assertRaises(ResourceException) as ctx:
|
||||
provider.apply(plan)
|
||||
self.assertEquals(load_mock.side_effect, ctx.exception)
|
||||
self.assertEquals(zone_retrieve_mock.side_effect, ctx.exception)
|
||||
|
||||
# Fails, bad auth
|
||||
load_mock.reset_mock()
|
||||
create_mock.reset_mock()
|
||||
load_mock.side_effect = \
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_create_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = \
|
||||
ResourceException('server error: zone not found')
|
||||
create_mock.side_effect = AuthException('unauthorized')
|
||||
zone_create_mock.side_effect = AuthException('unauthorized')
|
||||
with self.assertRaises(AuthException) as ctx:
|
||||
provider.apply(plan)
|
||||
self.assertEquals(create_mock.side_effect, ctx.exception)
|
||||
self.assertEquals(zone_create_mock.side_effect, ctx.exception)
|
||||
|
||||
# non-existant zone, create
|
||||
load_mock.reset_mock()
|
||||
create_mock.reset_mock()
|
||||
load_mock.side_effect = \
|
||||
# non-existent zone, create
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_create_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = \
|
||||
ResourceException('server error: zone not found')
|
||||
# ugh, need a mock zone with a mock prop since we're using getattr, we
|
||||
# can actually control side effects on `meth` with that.
|
||||
mock_zone = Mock()
|
||||
mock_zone.add_SRV = Mock()
|
||||
mock_zone.add_SRV.side_effect = [
|
||||
|
||||
zone_create_mock.side_effect = ['foo']
|
||||
# Test out the create rate-limit handling, then 9 successes
|
||||
record_create_mock.side_effect = [
|
||||
RateLimitException('boo', period=0),
|
||||
None,
|
||||
]
|
||||
create_mock.side_effect = [mock_zone]
|
||||
] + ([None] * 9)
|
||||
|
||||
got_n = provider.apply(plan)
|
||||
self.assertEquals(expected_n, got_n)
|
||||
|
||||
# Zone was created
|
||||
zone_create_mock.assert_has_calls([call('unit.tests')])
|
||||
# Checking that we got some of the expected records too
|
||||
record_create_mock.assert_has_calls([
|
||||
call('unit.tests', 'unit.tests', 'A', answers=[
|
||||
{'answer': ['1.2.3.4'], 'meta': {}}
|
||||
], filters=[], ttl=32),
|
||||
call('unit.tests', 'unit.tests', 'CAA', answers=[
|
||||
(0, 'issue', 'ca.unit.tests')
|
||||
], ttl=40),
|
||||
call('unit.tests', 'unit.tests', 'MX', answers=[
|
||||
(10, 'mx1.unit.tests.'), (20, 'mx2.unit.tests.')
|
||||
], ttl=35),
|
||||
])
|
||||
|
||||
# Update & delete
|
||||
load_mock.reset_mock()
|
||||
create_mock.reset_mock()
|
||||
nsone_zone = DummyZone(self.nsone_records + [{
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'short_answers': ['9.9.9.9'],
|
||||
'domain': 'delete-me.unit.tests.',
|
||||
}])
|
||||
nsone_zone.data['records'][0]['short_answers'][0] = '2.2.2.2'
|
||||
nsone_zone.loadRecord = Mock()
|
||||
zone_search = Mock()
|
||||
zone_search.return_value = [
|
||||
{
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_create_mock.reset_mock()
|
||||
|
||||
ns1_zone = {
|
||||
'records': self.ns1_records + [{
|
||||
'type': 'A',
|
||||
'ttl': 42,
|
||||
'short_answers': ['9.9.9.9'],
|
||||
'domain': 'delete-me.unit.tests.',
|
||||
}, {
|
||||
"domain": "geo.unit.tests",
|
||||
"zone": "unit.tests",
|
||||
"type": "A",
|
||||
"answers": [
|
||||
{'answer': ['1.1.1.1'], 'meta': {}},
|
||||
{'answer': ['1.2.3.4'],
|
||||
'meta': {'ca_province': ['ON']}},
|
||||
{'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
|
||||
{'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
|
||||
{'answer': ['4.5.6.7'],
|
||||
'meta': {'iso_region_code': ['NA-US-WA']}},
|
||||
"short_answers": [
|
||||
'1.1.1.1',
|
||||
'1.2.3.4',
|
||||
'2.3.4.5',
|
||||
'3.4.5.6',
|
||||
'4.5.6.7',
|
||||
],
|
||||
'tier': 3, # This flags it as advacned, full load required
|
||||
'ttl': 34,
|
||||
},
|
||||
]
|
||||
nsone_zone.search = zone_search
|
||||
load_mock.side_effect = [nsone_zone, nsone_zone]
|
||||
}],
|
||||
}
|
||||
ns1_zone['records'][0]['short_answers'][0] = '2.2.2.2'
|
||||
|
||||
record_retrieve_mock.side_effect = [{
|
||||
"domain": "geo.unit.tests",
|
||||
"zone": "unit.tests",
|
||||
"type": "A",
|
||||
"answers": [
|
||||
{'answer': ['1.1.1.1'], 'meta': {}},
|
||||
{'answer': ['1.2.3.4'],
|
||||
'meta': {'ca_province': ['ON']}},
|
||||
{'answer': ['2.3.4.5'], 'meta': {'us_state': ['NY']}},
|
||||
{'answer': ['3.4.5.6'], 'meta': {'country': ['US']}},
|
||||
{'answer': ['4.5.6.7'],
|
||||
'meta': {'iso_region_code': ['NA-US-WA']}},
|
||||
],
|
||||
'tier': 3,
|
||||
'ttl': 34,
|
||||
}]
|
||||
|
||||
zone_retrieve_mock.side_effect = [ns1_zone, ns1_zone]
|
||||
plan = provider.plan(desired)
|
||||
self.assertEquals(3, len(plan.changes))
|
||||
self.assertIsInstance(plan.changes[0], Update)
|
||||
self.assertIsInstance(plan.changes[2], Delete)
|
||||
# ugh, we need a mock record that can be returned from loadRecord for
|
||||
# the update and delete targets, we can add our side effects to that to
|
||||
# trigger rate limit handling
|
||||
mock_record = Mock()
|
||||
mock_record.update.side_effect = [
|
||||
# Shouldn't rely on order so just count classes
|
||||
classes = defaultdict(lambda: 0)
|
||||
for change in plan.changes:
|
||||
classes[change.__class__] += 1
|
||||
self.assertEquals(1, classes[Delete])
|
||||
self.assertEquals(2, classes[Update])
|
||||
|
||||
record_update_mock.side_effect = [
|
||||
RateLimitException('one', period=0),
|
||||
None,
|
||||
None,
|
||||
]
|
||||
mock_record.delete.side_effect = [
|
||||
record_delete_mock.side_effect = [
|
||||
RateLimitException('two', period=0),
|
||||
None,
|
||||
None,
|
||||
]
|
||||
nsone_zone.loadRecord.side_effect = [mock_record, mock_record,
|
||||
mock_record]
|
||||
|
||||
got_n = provider.apply(plan)
|
||||
self.assertEquals(3, got_n)
|
||||
nsone_zone.loadRecord.assert_has_calls([
|
||||
call('unit.tests', u'A'),
|
||||
call('geo', u'A'),
|
||||
call('delete-me', u'A'),
|
||||
])
|
||||
mock_record.assert_has_calls([
|
||||
call.update(answers=[{'answer': [u'1.2.3.4'], 'meta': {}}],
|
||||
filters=[],
|
||||
ttl=32),
|
||||
call.update(answers=[{u'answer': [u'1.2.3.4'], u'meta': {}}],
|
||||
filters=[],
|
||||
ttl=32),
|
||||
call.update(
|
||||
answers=[
|
||||
{u'answer': [u'101.102.103.104'], u'meta': {}},
|
||||
{u'answer': [u'101.102.103.105'], u'meta': {}},
|
||||
{
|
||||
u'answer': [u'201.202.203.204'],
|
||||
u'meta': {
|
||||
u'iso_region_code': [u'NA-US-NY']
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
record_update_mock.assert_has_calls([
|
||||
call('unit.tests', 'unit.tests', 'A', answers=[
|
||||
{'answer': ['1.2.3.4'], 'meta': {}}],
|
||||
filters=[],
|
||||
ttl=32),
|
||||
call('unit.tests', 'unit.tests', 'A', answers=[
|
||||
{'answer': ['1.2.3.4'], 'meta': {}}],
|
||||
filters=[],
|
||||
ttl=32),
|
||||
call('unit.tests', 'geo.unit.tests', 'A', answers=[
|
||||
{'answer': ['101.102.103.104'], 'meta': {}},
|
||||
{'answer': ['101.102.103.105'], 'meta': {}},
|
||||
{
|
||||
'answer': ['201.202.203.204'],
|
||||
'meta': {'iso_region_code': ['NA-US-NY']}
|
||||
}],
|
||||
filters=[
|
||||
{u'filter': u'shuffle', u'config': {}},
|
||||
{u'filter': u'geotarget_country', u'config': {}},
|
||||
{u'filter': u'select_first_n', u'config': {u'N': 1}},
|
||||
],
|
||||
ttl=34),
|
||||
call.delete(),
|
||||
call.delete()
|
||||
{'filter': 'shuffle', 'config': {}},
|
||||
{'filter': 'geotarget_country', 'config': {}},
|
||||
{'filter': 'select_first_n', 'config': {'N': 1}}],
|
||||
ttl=34)
|
||||
])
|
||||
|
||||
def test_escaping(self):
|
||||
@@ -462,7 +471,7 @@ class TestNs1Provider(TestCase):
|
||||
def test_data_for_CNAME(self):
|
||||
provider = Ns1Provider('test', 'api-key')
|
||||
|
||||
# answers from nsone
|
||||
# answers from ns1
|
||||
a_record = {
|
||||
'ttl': 31,
|
||||
'type': 'CNAME',
|
||||
@@ -476,7 +485,7 @@ class TestNs1Provider(TestCase):
|
||||
self.assertEqual(a_expected,
|
||||
provider._data_for_CNAME(a_record['type'], a_record))
|
||||
|
||||
# no answers from nsone
|
||||
# no answers from ns1
|
||||
b_record = {
|
||||
'ttl': 32,
|
||||
'type': 'CNAME',
|
||||
@@ -489,3 +498,46 @@ class TestNs1Provider(TestCase):
|
||||
}
|
||||
self.assertEqual(b_expected,
|
||||
provider._data_for_CNAME(b_record['type'], b_record))
|
||||
|
||||
|
||||
class TestNs1Client(TestCase):
|
||||
|
||||
@patch('ns1.rest.zones.Zones.retrieve')
|
||||
def test_retry_behavior(self, zone_retrieve_mock):
|
||||
client = Ns1Client('dummy-key')
|
||||
|
||||
# No retry required, just calls and is returned
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = ['foo']
|
||||
self.assertEquals('foo', client.zones_retrieve('unit.tests'))
|
||||
zone_retrieve_mock.assert_has_calls([call('unit.tests')])
|
||||
|
||||
# One retry required
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = [
|
||||
RateLimitException('boo', period=0),
|
||||
'foo'
|
||||
]
|
||||
self.assertEquals('foo', client.zones_retrieve('unit.tests'))
|
||||
zone_retrieve_mock.assert_has_calls([call('unit.tests')])
|
||||
|
||||
# Two retries required
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = [
|
||||
RateLimitException('boo', period=0),
|
||||
'foo'
|
||||
]
|
||||
self.assertEquals('foo', client.zones_retrieve('unit.tests'))
|
||||
zone_retrieve_mock.assert_has_calls([call('unit.tests')])
|
||||
|
||||
# Exhaust our retries
|
||||
zone_retrieve_mock.reset_mock()
|
||||
zone_retrieve_mock.side_effect = [
|
||||
RateLimitException('first', period=0),
|
||||
RateLimitException('boo', period=0),
|
||||
RateLimitException('boo', period=0),
|
||||
RateLimitException('last', period=0),
|
||||
]
|
||||
with self.assertRaises(RateLimitException) as ctx:
|
||||
client.zones_retrieve('unit.tests')
|
||||
self.assertEquals('last', text_type(ctx.exception))
|
||||
|
||||
@@ -382,64 +382,63 @@ class TestOvhProvider(TestCase):
|
||||
get_mock.side_effect = [[100], [101], [102], [103]]
|
||||
provider.apply(plan)
|
||||
wanted_calls = [
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'TXT',
|
||||
subDomain='txt', target=u'TXT text', ttl=1400),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'DKIM',
|
||||
subDomain='dkim', target=self.valid_dkim_key,
|
||||
ttl=1300),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'A',
|
||||
subDomain=u'', target=u'1.2.3.4', ttl=100),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'SRV',
|
||||
call('/domain/zone/unit.tests/record', fieldType='A',
|
||||
subDomain='', target='1.2.3.4', ttl=100),
|
||||
call('/domain/zone/unit.tests/record', fieldType='AAAA',
|
||||
subDomain='', target='1:1ec:1::1', ttl=200),
|
||||
call('/domain/zone/unit.tests/record', fieldType='MX',
|
||||
subDomain='', target='10 mx1.unit.tests.', ttl=400),
|
||||
call('/domain/zone/unit.tests/record', fieldType='SPF',
|
||||
subDomain='',
|
||||
target='v=spf1 include:unit.texts.redirect ~all',
|
||||
ttl=1000),
|
||||
call('/domain/zone/unit.tests/record', fieldType='SSHFP',
|
||||
subDomain='',
|
||||
target='1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73',
|
||||
ttl=1100),
|
||||
call('/domain/zone/unit.tests/record', fieldType='PTR',
|
||||
subDomain='4', target='unit.tests.', ttl=900),
|
||||
call('/domain/zone/unit.tests/record', fieldType='SRV',
|
||||
subDomain='_srv._tcp',
|
||||
target=u'10 20 30 foo-1.unit.tests.', ttl=800),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'SRV',
|
||||
target='10 20 30 foo-1.unit.tests.', ttl=800),
|
||||
call('/domain/zone/unit.tests/record', fieldType='SRV',
|
||||
subDomain='_srv._tcp',
|
||||
target=u'40 50 60 foo-2.unit.tests.', ttl=800),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'PTR',
|
||||
subDomain='4', target=u'unit.tests.', ttl=900),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'NS',
|
||||
subDomain='www3', target=u'ns3.unit.tests.', ttl=700),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'NS',
|
||||
subDomain='www3', target=u'ns4.unit.tests.', ttl=700),
|
||||
call(u'/domain/zone/unit.tests/record',
|
||||
fieldType=u'SSHFP', subDomain=u'', ttl=1100,
|
||||
target=u'1 1 bf6b6825d2977c511a475bbefb88a'
|
||||
u'ad54'
|
||||
u'a92ac73',
|
||||
),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'AAAA',
|
||||
subDomain=u'', target=u'1:1ec:1::1', ttl=200),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'MX',
|
||||
subDomain=u'', target=u'10 mx1.unit.tests.', ttl=400),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'CNAME',
|
||||
subDomain='www2', target=u'unit.tests.', ttl=300),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'SPF',
|
||||
subDomain=u'', ttl=1000,
|
||||
target=u'v=spf1 include:unit.texts.'
|
||||
u'redirect ~all',
|
||||
),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'A',
|
||||
subDomain='sub', target=u'1.2.3.4', ttl=200),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'NAPTR',
|
||||
subDomain='naptr', ttl=500,
|
||||
target=u'10 100 "S" "SIP+D2U" "!^.*$!sip:'
|
||||
u'info@bar'
|
||||
u'.example.com!" .'
|
||||
),
|
||||
call(u'/domain/zone/unit.tests/refresh')]
|
||||
target='40 50 60 foo-2.unit.tests.', ttl=800),
|
||||
call('/domain/zone/unit.tests/record', fieldType='DKIM',
|
||||
subDomain='dkim',
|
||||
target='p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxLaG'
|
||||
'16G4SaEcXVdiIxTg7gKSGbHKQLm30CHib1h9FzS9nkcyvQSyQj1r'
|
||||
'MFyqC//tft3ohx3nvJl+bGCWxdtLYDSmir9PW54e5CTdxEh8MWRk'
|
||||
'BO3StF6QG/tAh3aTGDmkqhIJGLb87iHvpmVKqURmEUzJPv5KPJfW'
|
||||
'LofADI+q9lQIDAQAB', ttl=1300),
|
||||
call('/domain/zone/unit.tests/record', fieldType='NAPTR',
|
||||
subDomain='naptr',
|
||||
target='10 100 "S" "SIP+D2U" "!^.*$!sip:[email protected]'
|
||||
'ple.com!" .', ttl=500),
|
||||
call('/domain/zone/unit.tests/record', fieldType='A',
|
||||
subDomain='sub', target='1.2.3.4', ttl=200),
|
||||
call('/domain/zone/unit.tests/record', fieldType='TXT',
|
||||
subDomain='txt', target='TXT text', ttl=1400),
|
||||
call('/domain/zone/unit.tests/record', fieldType='CNAME',
|
||||
subDomain='www2', target='unit.tests.', ttl=300),
|
||||
call('/domain/zone/unit.tests/record', fieldType='NS',
|
||||
subDomain='www3', target='ns3.unit.tests.', ttl=700),
|
||||
call('/domain/zone/unit.tests/record', fieldType='NS',
|
||||
subDomain='www3', target='ns4.unit.tests.', ttl=700),
|
||||
call('/domain/zone/unit.tests/refresh')]
|
||||
|
||||
post_mock.assert_has_calls(wanted_calls)
|
||||
|
||||
# Get for delete calls
|
||||
wanted_get_calls = [
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'TXT',
|
||||
subDomain='txt'),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'A',
|
||||
subDomain=u''),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'DKIM',
|
||||
subDomain='dkim'),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'A',
|
||||
subDomain=u''),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'A',
|
||||
subDomain='fake')]
|
||||
subDomain='fake'),
|
||||
call(u'/domain/zone/unit.tests/record', fieldType=u'TXT',
|
||||
subDomain='txt')]
|
||||
get_mock.assert_has_calls(wanted_get_calls)
|
||||
# 4 delete calls for update and delete
|
||||
delete_mock.assert_has_calls(
|
||||
|
||||
@@ -9,6 +9,7 @@ from json import loads, dumps
|
||||
from os.path import dirname, join
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import Record
|
||||
@@ -41,7 +42,7 @@ with open('./tests/fixtures/powerdns-full-data.json') as fh:
|
||||
class TestPowerDnsProvider(TestCase):
|
||||
|
||||
def test_provider(self):
|
||||
provider = PowerDnsProvider('test', 'non.existant', 'api-key',
|
||||
provider = PowerDnsProvider('test', 'non.existent', 'api-key',
|
||||
nameserver_values=['8.8.8.8.',
|
||||
'9.9.9.9.'])
|
||||
|
||||
@@ -52,7 +53,7 @@ class TestPowerDnsProvider(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone)
|
||||
self.assertTrue('unauthorized' in ctx.exception.message)
|
||||
self.assertTrue('unauthorized' in text_type(ctx.exception))
|
||||
|
||||
# General error
|
||||
with requests_mock() as mock:
|
||||
@@ -63,7 +64,7 @@ class TestPowerDnsProvider(TestCase):
|
||||
provider.populate(zone)
|
||||
self.assertEquals(502, ctx.exception.response.status_code)
|
||||
|
||||
# Non-existant zone doesn't populate anything
|
||||
# Non-existent zone doesn't populate anything
|
||||
with requests_mock() as mock:
|
||||
mock.get(ANY, status_code=422,
|
||||
json={'error': "Could not find domain 'unit.tests.'"})
|
||||
@@ -163,7 +164,7 @@ class TestPowerDnsProvider(TestCase):
|
||||
provider.apply(plan)
|
||||
|
||||
def test_small_change(self):
|
||||
provider = PowerDnsProvider('test', 'non.existant', 'api-key')
|
||||
provider = PowerDnsProvider('test', 'non.existent', 'api-key')
|
||||
|
||||
expected = Zone('unit.tests.', [])
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
@@ -203,7 +204,7 @@ class TestPowerDnsProvider(TestCase):
|
||||
|
||||
def test_existing_nameservers(self):
|
||||
ns_values = ['8.8.8.8.', '9.9.9.9.']
|
||||
provider = PowerDnsProvider('test', 'non.existant', 'api-key',
|
||||
provider = PowerDnsProvider('test', 'non.existent', 'api-key',
|
||||
nameserver_values=ns_values)
|
||||
|
||||
expected = Zone('unit.tests.', [])
|
||||
|
||||
@@ -7,8 +7,9 @@ from __future__ import absolute_import, division, print_function, \
|
||||
|
||||
import json
|
||||
import re
|
||||
from six import text_type
|
||||
from six.moves.urllib.parse import urlparse
|
||||
from unittest import TestCase
|
||||
from urlparse import urlparse
|
||||
|
||||
from requests import HTTPError
|
||||
from requests_mock import ANY, mock as requests_mock
|
||||
@@ -39,7 +40,6 @@ with open('./tests/fixtures/rackspace-sample-recordset-page2.json') as fh:
|
||||
|
||||
class TestRackspaceProvider(TestCase):
|
||||
def setUp(self):
|
||||
self.maxDiff = 1000
|
||||
with requests_mock() as mock:
|
||||
mock.post(ANY, status_code=200, text=AUTH_RESPONSE)
|
||||
self.provider = RackspaceProvider('identity', 'test', 'api-key',
|
||||
@@ -53,7 +53,7 @@ class TestRackspaceProvider(TestCase):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
zone = Zone('unit.tests.', [])
|
||||
self.provider.populate(zone)
|
||||
self.assertTrue('unauthorized' in ctx.exception.message)
|
||||
self.assertTrue('unauthorized' in text_type(ctx.exception))
|
||||
self.assertTrue(mock.called_once)
|
||||
|
||||
def test_server_error(self):
|
||||
@@ -792,13 +792,13 @@ class TestRackspaceProvider(TestCase):
|
||||
ExpectedUpdates = {
|
||||
"records": [{
|
||||
"name": "unit.tests",
|
||||
"id": "A-222222",
|
||||
"data": "1.2.3.5",
|
||||
"id": "A-111111",
|
||||
"data": "1.2.3.4",
|
||||
"ttl": 3600
|
||||
}, {
|
||||
"name": "unit.tests",
|
||||
"id": "A-111111",
|
||||
"data": "1.2.3.4",
|
||||
"id": "A-222222",
|
||||
"data": "1.2.3.5",
|
||||
"ttl": 3600
|
||||
}, {
|
||||
"name": "unit.tests",
|
||||
|
||||
@@ -7,12 +7,14 @@ from __future__ import absolute_import, division, print_function, \
|
||||
|
||||
from botocore.exceptions import ClientError
|
||||
from botocore.stub import ANY, Stubber
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
from mock import patch
|
||||
|
||||
from octodns.record import Create, Delete, Record, Update
|
||||
from octodns.provider.route53 import Route53Provider, _Route53GeoDefault, \
|
||||
_Route53GeoRecord, _Route53Record, _mod_keyer, _octal_replace
|
||||
_Route53DynamicValue, _Route53GeoRecord, _Route53Record, _mod_keyer, \
|
||||
_octal_replace
|
||||
from octodns.zone import Zone
|
||||
|
||||
from helpers import GeoProvider
|
||||
@@ -502,7 +504,7 @@ class TestRoute53Provider(TestCase):
|
||||
'ResourceRecords': [{
|
||||
'Value': '10 smtp-1.unit.tests.',
|
||||
}, {
|
||||
'Value': '20 smtp-2.unit.tests.',
|
||||
'Value': '20 smtp-2.unit.tests.',
|
||||
}],
|
||||
'TTL': 64,
|
||||
}, {
|
||||
@@ -699,18 +701,6 @@ class TestRoute53Provider(TestCase):
|
||||
'TTL': 61,
|
||||
'Type': 'A'
|
||||
}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'GeoLocation': {'CountryCode': 'US',
|
||||
'SubdivisionCode': 'CA'},
|
||||
'HealthCheckId': u'44',
|
||||
'Name': 'unit.tests.',
|
||||
'ResourceRecords': [{'Value': '7.2.3.4'}],
|
||||
'SetIdentifier': 'NA-US-CA',
|
||||
'TTL': 61,
|
||||
'Type': 'A'
|
||||
}
|
||||
}, {
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
@@ -734,6 +724,18 @@ class TestRoute53Provider(TestCase):
|
||||
'TTL': 61,
|
||||
'Type': 'A'
|
||||
}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'GeoLocation': {'CountryCode': 'US',
|
||||
'SubdivisionCode': 'CA'},
|
||||
'HealthCheckId': u'44',
|
||||
'Name': 'unit.tests.',
|
||||
'ResourceRecords': [{'Value': '7.2.3.4'}],
|
||||
'SetIdentifier': 'NA-US-CA',
|
||||
'TTL': 61,
|
||||
'Type': 'A'
|
||||
}
|
||||
}, {
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
@@ -874,6 +876,25 @@ class TestRoute53Provider(TestCase):
|
||||
'CallerReference': ANY,
|
||||
})
|
||||
|
||||
list_resource_record_sets_resp = {
|
||||
'ResourceRecordSets': [{
|
||||
'Name': 'a.unit.tests.',
|
||||
'Type': 'A',
|
||||
'GeoLocation': {
|
||||
'ContinentCode': 'NA',
|
||||
},
|
||||
'ResourceRecords': [{
|
||||
'Value': '2.2.3.4',
|
||||
}],
|
||||
'TTL': 61,
|
||||
}],
|
||||
'IsTruncated': False,
|
||||
'MaxItems': '100',
|
||||
}
|
||||
stubber.add_response('list_resource_record_sets',
|
||||
list_resource_record_sets_resp,
|
||||
{'HostedZoneId': 'z42'})
|
||||
|
||||
stubber.add_response('list_health_checks',
|
||||
{
|
||||
'HealthChecks': self.health_checks,
|
||||
@@ -1236,7 +1257,7 @@ class TestRoute53Provider(TestCase):
|
||||
'HealthCheckId': '44',
|
||||
})
|
||||
change = Create(record)
|
||||
provider._mod_Create(change, 'z43')
|
||||
provider._mod_Create(change, 'z43', [])
|
||||
stubber.assert_no_pending_responses()
|
||||
|
||||
# gc through _mod_Update
|
||||
@@ -1245,7 +1266,7 @@ class TestRoute53Provider(TestCase):
|
||||
})
|
||||
# first record is ignored for our purposes, we have to pass something
|
||||
change = Update(record, record)
|
||||
provider._mod_Create(change, 'z43')
|
||||
provider._mod_Create(change, 'z43', [])
|
||||
stubber.assert_no_pending_responses()
|
||||
|
||||
# gc through _mod_Delete, expect 3 to go away, can't check order
|
||||
@@ -1260,7 +1281,7 @@ class TestRoute53Provider(TestCase):
|
||||
'HealthCheckId': ANY,
|
||||
})
|
||||
change = Delete(record)
|
||||
provider._mod_Delete(change, 'z43')
|
||||
provider._mod_Delete(change, 'z43', [])
|
||||
stubber.assert_no_pending_responses()
|
||||
|
||||
# gc only AAAA, leave the A's alone
|
||||
@@ -1653,7 +1674,7 @@ class TestRoute53Provider(TestCase):
|
||||
desired.add_record(record)
|
||||
list_resource_record_sets_resp = {
|
||||
'ResourceRecordSets': [{
|
||||
# other name
|
||||
# Not dynamic value and other name
|
||||
'Name': 'unit.tests.',
|
||||
'Type': 'A',
|
||||
'GeoLocation': {
|
||||
@@ -1663,17 +1684,21 @@ class TestRoute53Provider(TestCase):
|
||||
'Value': '1.2.3.4',
|
||||
}],
|
||||
'TTL': 61,
|
||||
# All the non-matches have a different Id so we'll fail if they
|
||||
# match
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# matching name, other type
|
||||
# Not dynamic value, matching name, other type
|
||||
'Name': 'a.unit.tests.',
|
||||
'Type': 'AAAA',
|
||||
'ResourceRecords': [{
|
||||
'Value': '2001:0db8:3c4d:0015:0000:0000:1a2f:1a4b'
|
||||
}],
|
||||
'TTL': 61,
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# default value pool
|
||||
'Name': '_octodns-default-pool.a.unit.tests.',
|
||||
'Name': '_octodns-default-value.a.unit.tests.',
|
||||
'Type': 'A',
|
||||
'GeoLocation': {
|
||||
'CountryCode': '*',
|
||||
@@ -1682,6 +1707,37 @@ class TestRoute53Provider(TestCase):
|
||||
'Value': '1.2.3.4',
|
||||
}],
|
||||
'TTL': 61,
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# different record
|
||||
'Name': '_octodns-two-value.other.unit.tests.',
|
||||
'Type': 'A',
|
||||
'GeoLocation': {
|
||||
'CountryCode': '*',
|
||||
},
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.2.3.4',
|
||||
}],
|
||||
'TTL': 61,
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# same everything, but different type
|
||||
'Name': '_octodns-one-value.a.unit.tests.',
|
||||
'Type': 'AAAA',
|
||||
'ResourceRecords': [{
|
||||
'Value': '2001:0db8:3c4d:0015:0000:0000:1a2f:1a4b'
|
||||
}],
|
||||
'TTL': 61,
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# same everything, sub
|
||||
'Name': '_octodns-one-value.sub.a.unit.tests.',
|
||||
'Type': 'A',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.2.3.4',
|
||||
}],
|
||||
'TTL': 61,
|
||||
'HealthCheckId': '33',
|
||||
}, {
|
||||
# match
|
||||
'Name': '_octodns-one-value.a.unit.tests.',
|
||||
@@ -1804,46 +1860,51 @@ class TestRoute53Provider(TestCase):
|
||||
|
||||
# _get_test_plan() returns a plan with 11 modifications, 17 RRs
|
||||
|
||||
@patch('octodns.provider.route53.Route53Provider._load_records')
|
||||
@patch('octodns.provider.route53.Route53Provider._really_apply')
|
||||
def test_apply_1(self, really_apply_mock):
|
||||
def test_apply_1(self, really_apply_mock, _):
|
||||
|
||||
# 18 RRs with max of 19 should only get applied in one call
|
||||
provider, plan = self._get_test_plan(19)
|
||||
provider.apply(plan)
|
||||
really_apply_mock.assert_called_once()
|
||||
|
||||
@patch('octodns.provider.route53.Route53Provider._load_records')
|
||||
@patch('octodns.provider.route53.Route53Provider._really_apply')
|
||||
def test_apply_2(self, really_apply_mock):
|
||||
def test_apply_2(self, really_apply_mock, _):
|
||||
|
||||
# 18 RRs with max of 17 should only get applied in two calls
|
||||
provider, plan = self._get_test_plan(18)
|
||||
provider.apply(plan)
|
||||
self.assertEquals(2, really_apply_mock.call_count)
|
||||
|
||||
@patch('octodns.provider.route53.Route53Provider._load_records')
|
||||
@patch('octodns.provider.route53.Route53Provider._really_apply')
|
||||
def test_apply_3(self, really_apply_mock):
|
||||
def test_apply_3(self, really_apply_mock, _):
|
||||
|
||||
# with a max of seven modifications, four calls
|
||||
# with a max of seven modifications, three calls
|
||||
provider, plan = self._get_test_plan(7)
|
||||
provider.apply(plan)
|
||||
self.assertEquals(4, really_apply_mock.call_count)
|
||||
self.assertEquals(3, really_apply_mock.call_count)
|
||||
|
||||
@patch('octodns.provider.route53.Route53Provider._load_records')
|
||||
@patch('octodns.provider.route53.Route53Provider._really_apply')
|
||||
def test_apply_4(self, really_apply_mock):
|
||||
def test_apply_4(self, really_apply_mock, _):
|
||||
|
||||
# with a max of 11 modifications, two calls
|
||||
provider, plan = self._get_test_plan(11)
|
||||
provider.apply(plan)
|
||||
self.assertEquals(2, really_apply_mock.call_count)
|
||||
|
||||
@patch('octodns.provider.route53.Route53Provider._load_records')
|
||||
@patch('octodns.provider.route53.Route53Provider._really_apply')
|
||||
def test_apply_bad(self, really_apply_mock):
|
||||
def test_apply_bad(self, really_apply_mock, _):
|
||||
|
||||
# with a max of 1 modifications, fail
|
||||
provider, plan = self._get_test_plan(1)
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
provider.apply(plan)
|
||||
self.assertTrue('modifications' in ctx.exception.message)
|
||||
self.assertTrue('modifications' in text_type(ctx.exception))
|
||||
|
||||
def test_semicolon_fixup(self):
|
||||
provider = Route53Provider('test', 'abc', '123')
|
||||
@@ -1939,6 +2000,12 @@ class TestRoute53Provider(TestCase):
|
||||
}], [r.data for r in record.dynamic.rules])
|
||||
|
||||
|
||||
class DummyProvider(object):
|
||||
|
||||
def get_health_check_id(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
|
||||
class TestRoute53Records(TestCase):
|
||||
existing = Zone('unit.tests.', [])
|
||||
record_a = Record.new(existing, '', {
|
||||
@@ -2005,11 +2072,6 @@ class TestRoute53Records(TestCase):
|
||||
e = _Route53GeoDefault(None, self.record_a, False)
|
||||
self.assertNotEquals(a, e)
|
||||
|
||||
class DummyProvider(object):
|
||||
|
||||
def get_health_check_id(self, *args, **kwargs):
|
||||
return None
|
||||
|
||||
provider = DummyProvider()
|
||||
f = _Route53GeoRecord(provider, self.record_a, 'NA-US',
|
||||
self.record_a.geo['NA-US'], False)
|
||||
@@ -2029,6 +2091,153 @@ class TestRoute53Records(TestCase):
|
||||
e.__repr__()
|
||||
f.__repr__()
|
||||
|
||||
def test_route53_record_ordering(self):
|
||||
# Matches
|
||||
a = _Route53Record(None, self.record_a, False)
|
||||
b = _Route53Record(None, self.record_a, False)
|
||||
self.assertTrue(a == b)
|
||||
self.assertFalse(a != b)
|
||||
self.assertFalse(a < b)
|
||||
self.assertTrue(a <= b)
|
||||
self.assertFalse(a > b)
|
||||
self.assertTrue(a >= b)
|
||||
|
||||
# Change the fqdn is greater
|
||||
fqdn = _Route53Record(None, self.record_a, False,
|
||||
fqdn_override='other')
|
||||
self.assertFalse(a == fqdn)
|
||||
self.assertTrue(a != fqdn)
|
||||
self.assertFalse(a < fqdn)
|
||||
self.assertFalse(a <= fqdn)
|
||||
self.assertTrue(a > fqdn)
|
||||
self.assertTrue(a >= fqdn)
|
||||
|
||||
provider = DummyProvider()
|
||||
geo_a = _Route53GeoRecord(provider, self.record_a, 'NA-US',
|
||||
self.record_a.geo['NA-US'], False)
|
||||
geo_b = _Route53GeoRecord(provider, self.record_a, 'NA-US',
|
||||
self.record_a.geo['NA-US'], False)
|
||||
self.assertTrue(geo_a == geo_b)
|
||||
self.assertFalse(geo_a != geo_b)
|
||||
self.assertFalse(geo_a < geo_b)
|
||||
self.assertTrue(geo_a <= geo_b)
|
||||
self.assertFalse(geo_a > geo_b)
|
||||
self.assertTrue(geo_a >= geo_b)
|
||||
|
||||
# Other base
|
||||
geo_fqdn = _Route53GeoRecord(provider, self.record_a, 'NA-US',
|
||||
self.record_a.geo['NA-US'], False)
|
||||
geo_fqdn.fqdn = 'other'
|
||||
self.assertFalse(geo_a == geo_fqdn)
|
||||
self.assertTrue(geo_a != geo_fqdn)
|
||||
self.assertFalse(geo_a < geo_fqdn)
|
||||
self.assertFalse(geo_a <= geo_fqdn)
|
||||
self.assertTrue(geo_a > geo_fqdn)
|
||||
self.assertTrue(geo_a >= geo_fqdn)
|
||||
|
||||
# Other class
|
||||
self.assertFalse(a == geo_a)
|
||||
self.assertTrue(a != geo_a)
|
||||
self.assertFalse(a < geo_a)
|
||||
self.assertFalse(a <= geo_a)
|
||||
self.assertTrue(a > geo_a)
|
||||
self.assertTrue(a >= geo_a)
|
||||
|
||||
def test_dynamic_value_delete(self):
|
||||
provider = DummyProvider()
|
||||
geo = _Route53DynamicValue(provider, self.record_a, 'iad', '2.2.2.2',
|
||||
1, 0, False)
|
||||
|
||||
rrset = {
|
||||
'HealthCheckId': 'x12346z',
|
||||
'Name': '_octodns-iad-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '2.2.2.2'
|
||||
}],
|
||||
'SetIdentifier': 'iad-000',
|
||||
'TTL': 99,
|
||||
'Type': 'A',
|
||||
'Weight': 1,
|
||||
}
|
||||
|
||||
candidates = [
|
||||
# Empty, will test no SetIdentifier
|
||||
{},
|
||||
# Non-matching
|
||||
{
|
||||
'SetIdentifier': 'not-a-match',
|
||||
},
|
||||
# Same set-id, different name
|
||||
{
|
||||
'Name': 'not-a-match',
|
||||
'SetIdentifier': 'x12346z',
|
||||
},
|
||||
rrset,
|
||||
]
|
||||
|
||||
# Provide a matching rrset so that we'll just use it for the delete
|
||||
# rathr than building up an almost identical one, note the way we'll
|
||||
# know that we got the one we passed in is that it'll have a
|
||||
# HealthCheckId and one that was created wouldn't since DummyProvider
|
||||
# stubs out the lookup for them
|
||||
mod = geo.mod('DELETE', candidates)
|
||||
self.assertEquals('x12346z', mod['ResourceRecordSet']['HealthCheckId'])
|
||||
|
||||
# If we don't provide the candidate rrsets we get back exactly what we
|
||||
# put in minus the healthcheck
|
||||
rrset['HealthCheckId'] = None
|
||||
mod = geo.mod('DELETE', [])
|
||||
self.assertEquals(rrset, mod['ResourceRecordSet'])
|
||||
|
||||
def test_geo_delete(self):
|
||||
provider = DummyProvider()
|
||||
geo = _Route53GeoRecord(provider, self.record_a, 'NA-US',
|
||||
self.record_a.geo['NA-US'], False)
|
||||
|
||||
rrset = {
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'US'
|
||||
},
|
||||
'HealthCheckId': 'x12346z',
|
||||
'Name': 'unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '2.2.2.2'
|
||||
}, {
|
||||
'Value': '3.3.3.3'
|
||||
}],
|
||||
'SetIdentifier': 'NA-US',
|
||||
'TTL': 99,
|
||||
'Type': 'A'
|
||||
}
|
||||
|
||||
candidates = [
|
||||
# Empty, will test no SetIdentifier
|
||||
{},
|
||||
{
|
||||
'SetIdentifier': 'not-a-match',
|
||||
},
|
||||
# Same set-id, different name
|
||||
{
|
||||
'Name': 'not-a-match',
|
||||
'SetIdentifier': 'x12346z',
|
||||
},
|
||||
rrset,
|
||||
]
|
||||
|
||||
# Provide a matching rrset so that we'll just use it for the delete
|
||||
# rathr than building up an almost identical one, note the way we'll
|
||||
# know that we got the one we passed in is that it'll have a
|
||||
# HealthCheckId and one that was created wouldn't since DummyProvider
|
||||
# stubs out the lookup for them
|
||||
mod = geo.mod('DELETE', candidates)
|
||||
self.assertEquals('x12346z', mod['ResourceRecordSet']['HealthCheckId'])
|
||||
|
||||
# If we don't provide the candidate rrsets we get back exactly what we
|
||||
# put in minus the healthcheck
|
||||
del rrset['HealthCheckId']
|
||||
mod = geo.mod('DELETE', [])
|
||||
self.assertEquals(rrset, mod['ResourceRecordSet'])
|
||||
|
||||
def test_new_dynamic(self):
|
||||
provider = Route53Provider('test', 'abc', '123')
|
||||
|
||||
@@ -2050,136 +2259,38 @@ class TestRoute53Records(TestCase):
|
||||
creating=True)
|
||||
self.assertEquals(18, len(route53_records))
|
||||
|
||||
expected_mods = [r.mod('CREATE', []) for r in route53_records]
|
||||
# Sort so that we get a consistent order and don't rely on set ordering
|
||||
expected_mods.sort(key=_mod_keyer)
|
||||
|
||||
# Convert the route53_records into mods
|
||||
self.assertEquals([{
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-ap-southeast-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.4.1.2'}],
|
||||
'SetIdentifier': 'ap-southeast-1-001',
|
||||
'ResourceRecords': [{'Value': '1.4.1.1'}],
|
||||
'SetIdentifier': 'ap-southeast-1-000',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 2
|
||||
}
|
||||
'Weight': 2}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-ap-southeast-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.4.1.1'}],
|
||||
'SetIdentifier': 'ap-southeast-1-000',
|
||||
'ResourceRecords': [{'Value': '1.4.1.2'}],
|
||||
'SetIdentifier': 'ap-southeast-1-001',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 2
|
||||
}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'
|
||||
},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'JP'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '0-ap-southeast-1-AS-JP',
|
||||
'Type': 'A'
|
||||
}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'US',
|
||||
'SubdivisionCode': 'FL',
|
||||
},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '1-eu-central-1-NA-US-FL',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': '*'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '2-us-east-1-None',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'SECONDARY',
|
||||
'Name': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'ap-southeast-1-Secondary-us-east-1',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'CN'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '0-ap-southeast-1-AS-CN',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-value.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'PRIMARY',
|
||||
'Name': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'us-east-1-Primary',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'ContinentCode': 'EU'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '1-eu-central-1-EU',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-value.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'PRIMARY',
|
||||
'Name': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'eu-central-1-Primary',
|
||||
'Type': 'A'}
|
||||
'Weight': 2}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'Name': '_octodns-default-pool.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.1.2.1'},
|
||||
{
|
||||
'Value': '1.1.2.2'}],
|
||||
'ResourceRecords': [
|
||||
{'Value': '1.1.2.1'},
|
||||
{'Value': '1.1.2.2'}],
|
||||
'TTL': 60,
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
@@ -2187,56 +2298,41 @@ class TestRoute53Records(TestCase):
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-eu-central-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.3.1.2'}],
|
||||
'ResourceRecords': [{'Value': '1.3.1.1'}],
|
||||
'SetIdentifier': 'eu-central-1-000',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-eu-central-1-value.unit.tests.',
|
||||
'ResourceRecords': [{'Value': '1.3.1.2'}],
|
||||
'SetIdentifier': 'eu-central-1-001',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-eu-central-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.3.1.1'}],
|
||||
'SetIdentifier': 'eu-central-1-000',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-default-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'SECONDARY',
|
||||
'Name': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'us-east-1-Secondary-default',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-us-east-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.5.1.2'}],
|
||||
'SetIdentifier': 'us-east-1-001',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-us-east-1-value.unit.tests.',
|
||||
'ResourceRecords': [{
|
||||
'Value': '1.5.1.1'}],
|
||||
'ResourceRecords': [{'Value': '1.5.1.1'}],
|
||||
'SetIdentifier': 'us-east-1-000',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'HealthCheckId': 'hc42',
|
||||
'Name': '_octodns-us-east-1-value.unit.tests.',
|
||||
'ResourceRecords': [{'Value': '1.5.1.2'}],
|
||||
'SetIdentifier': 'us-east-1-001',
|
||||
'TTL': 60,
|
||||
'Type': 'A',
|
||||
'Weight': 1}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
@@ -2248,6 +2344,39 @@ class TestRoute53Records(TestCase):
|
||||
'Name': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'ap-southeast-1-Primary',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-value.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'PRIMARY',
|
||||
'Name': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'eu-central-1-Primary',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-value.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'PRIMARY',
|
||||
'Name': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'us-east-1-Primary',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'SECONDARY',
|
||||
'Name': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'ap-southeast-1-Secondary-us-east-1',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
@@ -2259,7 +2388,79 @@ class TestRoute53Records(TestCase):
|
||||
'Name': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'eu-central-1-Secondary-us-east-1',
|
||||
'Type': 'A'}
|
||||
}], [r.mod('CREATE') for r in route53_records])
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-default-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'Failover': 'SECONDARY',
|
||||
'Name': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'SetIdentifier': 'us-east-1-Secondary-default',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'CN'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '0-ap-southeast-1-AS-CN',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-ap-southeast-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'JP'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '0-ap-southeast-1-AS-JP',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'ContinentCode': 'EU'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '1-eu-central-1-EU',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-eu-central-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': 'US',
|
||||
'SubdivisionCode': 'FL'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '1-eu-central-1-NA-US-FL',
|
||||
'Type': 'A'}
|
||||
}, {
|
||||
'Action': 'CREATE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': {
|
||||
'DNSName': '_octodns-us-east-1-pool.unit.tests.',
|
||||
'EvaluateTargetHealth': True,
|
||||
'HostedZoneId': 'z45'},
|
||||
'GeoLocation': {
|
||||
'CountryCode': '*'},
|
||||
'Name': 'unit.tests.',
|
||||
'SetIdentifier': '2-us-east-1-None',
|
||||
'Type': 'A'}
|
||||
}], expected_mods)
|
||||
|
||||
for route53_record in route53_records:
|
||||
# Smoke test stringification
|
||||
@@ -2270,7 +2471,7 @@ class TestModKeyer(TestCase):
|
||||
|
||||
def test_mod_keyer(self):
|
||||
|
||||
# First "column"
|
||||
# First "column" is the action priority for C/R/U
|
||||
|
||||
# Deletes come first
|
||||
self.assertEquals((0, 0, 'something'), _mod_keyer({
|
||||
@@ -2288,8 +2489,8 @@ class TestModKeyer(TestCase):
|
||||
}
|
||||
}))
|
||||
|
||||
# Then upserts
|
||||
self.assertEquals((2, 0, 'last'), _mod_keyer({
|
||||
# Upserts are the same as creates
|
||||
self.assertEquals((1, 0, 'last'), _mod_keyer({
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
'Name': 'last',
|
||||
@@ -2299,7 +2500,7 @@ class TestModKeyer(TestCase):
|
||||
# Second "column" value records tested above
|
||||
|
||||
# AliasTarget primary second (to value)
|
||||
self.assertEquals((0, 1, 'thing'), _mod_keyer({
|
||||
self.assertEquals((0, -1, 'thing'), _mod_keyer({
|
||||
'Action': 'DELETE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': 'some-target',
|
||||
@@ -2308,8 +2509,17 @@ class TestModKeyer(TestCase):
|
||||
}
|
||||
}))
|
||||
|
||||
self.assertEquals((1, 1, 'thing'), _mod_keyer({
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': 'some-target',
|
||||
'Failover': 'PRIMARY',
|
||||
'Name': 'thing',
|
||||
}
|
||||
}))
|
||||
|
||||
# AliasTarget secondary third
|
||||
self.assertEquals((0, 2, 'thing'), _mod_keyer({
|
||||
self.assertEquals((0, -2, 'thing'), _mod_keyer({
|
||||
'Action': 'DELETE',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': 'some-target',
|
||||
@@ -2318,8 +2528,17 @@ class TestModKeyer(TestCase):
|
||||
}
|
||||
}))
|
||||
|
||||
self.assertEquals((1, 2, 'thing'), _mod_keyer({
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
'AliasTarget': 'some-target',
|
||||
'Failover': 'SECONDARY',
|
||||
'Name': 'thing',
|
||||
}
|
||||
}))
|
||||
|
||||
# GeoLocation fourth
|
||||
self.assertEquals((0, 3, 'some-id'), _mod_keyer({
|
||||
self.assertEquals((0, -3, 'some-id'), _mod_keyer({
|
||||
'Action': 'DELETE',
|
||||
'ResourceRecordSet': {
|
||||
'GeoLocation': 'some-target',
|
||||
@@ -2327,4 +2546,12 @@ class TestModKeyer(TestCase):
|
||||
}
|
||||
}))
|
||||
|
||||
self.assertEquals((1, 3, 'some-id'), _mod_keyer({
|
||||
'Action': 'UPSERT',
|
||||
'ResourceRecordSet': {
|
||||
'GeoLocation': 'some-target',
|
||||
'SetIdentifier': 'some-id',
|
||||
}
|
||||
}))
|
||||
|
||||
# The third "column" has already been tested above, Name/SetIdentifier
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from unittest import TestCase
|
||||
from six import text_type
|
||||
|
||||
import requests_mock
|
||||
|
||||
from octodns.provider.selectel import SelectelProvider
|
||||
from octodns.record import Record, Update
|
||||
from octodns.zone import Zone
|
||||
|
||||
|
||||
class TestSelectelProvider(TestCase):
|
||||
API_URL = 'https://api.selectel.ru/domains/v1'
|
||||
|
||||
api_record = []
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
expected = set()
|
||||
|
||||
domain = [{"name": "unit.tests", "id": 100000}]
|
||||
|
||||
# A, subdomain=''
|
||||
api_record.append({
|
||||
'type': 'A',
|
||||
'ttl': 100,
|
||||
'content': '1.2.3.4',
|
||||
'name': 'unit.tests',
|
||||
'id': 1
|
||||
})
|
||||
expected.add(Record.new(zone, '', {
|
||||
'ttl': 100,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
}))
|
||||
|
||||
# A, subdomain='sub'
|
||||
api_record.append({
|
||||
'type': 'A',
|
||||
'ttl': 200,
|
||||
'content': '1.2.3.4',
|
||||
'name': 'sub.unit.tests',
|
||||
'id': 2
|
||||
})
|
||||
expected.add(Record.new(zone, 'sub', {
|
||||
'ttl': 200,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
}))
|
||||
|
||||
# CNAME
|
||||
api_record.append({
|
||||
'type': 'CNAME',
|
||||
'ttl': 300,
|
||||
'content': 'unit.tests',
|
||||
'name': 'www2.unit.tests',
|
||||
'id': 3
|
||||
})
|
||||
expected.add(Record.new(zone, 'www2', {
|
||||
'ttl': 300,
|
||||
'type': 'CNAME',
|
||||
'value': 'unit.tests.',
|
||||
}))
|
||||
|
||||
# MX
|
||||
api_record.append({
|
||||
'type': 'MX',
|
||||
'ttl': 400,
|
||||
'content': 'mx1.unit.tests',
|
||||
'priority': 10,
|
||||
'name': 'unit.tests',
|
||||
'id': 4
|
||||
})
|
||||
expected.add(Record.new(zone, '', {
|
||||
'ttl': 400,
|
||||
'type': 'MX',
|
||||
'values': [{
|
||||
'preference': 10,
|
||||
'exchange': 'mx1.unit.tests.',
|
||||
}]
|
||||
}))
|
||||
|
||||
# NS
|
||||
api_record.append({
|
||||
'type': 'NS',
|
||||
'ttl': 600,
|
||||
'content': 'ns1.unit.tests',
|
||||
'name': 'unit.tests.',
|
||||
'id': 6
|
||||
})
|
||||
api_record.append({
|
||||
'type': 'NS',
|
||||
'ttl': 600,
|
||||
'content': 'ns2.unit.tests',
|
||||
'name': 'unit.tests',
|
||||
'id': 7
|
||||
})
|
||||
expected.add(Record.new(zone, '', {
|
||||
'ttl': 600,
|
||||
'type': 'NS',
|
||||
'values': ['ns1.unit.tests.', 'ns2.unit.tests.'],
|
||||
}))
|
||||
|
||||
# NS with sub
|
||||
api_record.append({
|
||||
'type': 'NS',
|
||||
'ttl': 700,
|
||||
'content': 'ns3.unit.tests',
|
||||
'name': 'www3.unit.tests',
|
||||
'id': 8
|
||||
})
|
||||
api_record.append({
|
||||
'type': 'NS',
|
||||
'ttl': 700,
|
||||
'content': 'ns4.unit.tests',
|
||||
'name': 'www3.unit.tests',
|
||||
'id': 9
|
||||
})
|
||||
expected.add(Record.new(zone, 'www3', {
|
||||
'ttl': 700,
|
||||
'type': 'NS',
|
||||
'values': ['ns3.unit.tests.', 'ns4.unit.tests.'],
|
||||
}))
|
||||
|
||||
# SRV
|
||||
api_record.append({
|
||||
'type': 'SRV',
|
||||
'ttl': 800,
|
||||
'target': 'foo-1.unit.tests',
|
||||
'weight': 20,
|
||||
'priority': 10,
|
||||
'port': 30,
|
||||
'id': 10,
|
||||
'name': '_srv._tcp.unit.tests'
|
||||
})
|
||||
api_record.append({
|
||||
'type': 'SRV',
|
||||
'ttl': 800,
|
||||
'target': 'foo-2.unit.tests',
|
||||
'name': '_srv._tcp.unit.tests',
|
||||
'weight': 50,
|
||||
'priority': 40,
|
||||
'port': 60,
|
||||
'id': 11
|
||||
})
|
||||
expected.add(Record.new(zone, '_srv._tcp', {
|
||||
'ttl': 800,
|
||||
'type': 'SRV',
|
||||
'values': [{
|
||||
'priority': 10,
|
||||
'weight': 20,
|
||||
'port': 30,
|
||||
'target': 'foo-1.unit.tests.',
|
||||
}, {
|
||||
'priority': 40,
|
||||
'weight': 50,
|
||||
'port': 60,
|
||||
'target': 'foo-2.unit.tests.',
|
||||
}]
|
||||
}))
|
||||
|
||||
# AAAA
|
||||
aaaa_record = {
|
||||
'type': 'AAAA',
|
||||
'ttl': 200,
|
||||
'content': '1:1ec:1::1',
|
||||
'name': 'unit.tests',
|
||||
'id': 15
|
||||
}
|
||||
api_record.append(aaaa_record)
|
||||
expected.add(Record.new(zone, '', {
|
||||
'ttl': 200,
|
||||
'type': 'AAAA',
|
||||
'value': '1:1ec:1::1',
|
||||
}))
|
||||
|
||||
# TXT
|
||||
api_record.append({
|
||||
'type': 'TXT',
|
||||
'ttl': 300,
|
||||
'content': 'little text',
|
||||
'name': 'text.unit.tests',
|
||||
'id': 16
|
||||
})
|
||||
expected.add(Record.new(zone, 'text', {
|
||||
'ttl': 200,
|
||||
'type': 'TXT',
|
||||
'value': 'little text',
|
||||
}))
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_populate(self, fake_http):
|
||||
zone = Zone('unit.tests.', [])
|
||||
fake_http.get('{}/unit.tests/records/'.format(self.API_URL),
|
||||
json=self.api_record)
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.api_record))})
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
|
||||
provider = SelectelProvider(123, 'secret_token')
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertEquals(self.expected, zone.records)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_populate_invalid_record(self, fake_http):
|
||||
more_record = self.api_record
|
||||
more_record.append({"name": "unit.tests",
|
||||
"id": 100001,
|
||||
"content": "support.unit.tests.",
|
||||
"ttl": 300, "ns": "ns1.unit.tests",
|
||||
"type": "SOA",
|
||||
"email": "[email protected]"})
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
fake_http.get('{}/unit.tests/records/'.format(self.API_URL),
|
||||
json=more_record)
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.api_record))})
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
|
||||
zone.add_record(Record.new(self.zone, 'unsup', {
|
||||
'ttl': 200,
|
||||
'type': 'NAPTR',
|
||||
'value': {
|
||||
'order': 40,
|
||||
'preference': 70,
|
||||
'flags': 'U',
|
||||
'service': 'SIP+D2U',
|
||||
'regexp': '!^.*$!sip:[email protected]!',
|
||||
'replacement': '.',
|
||||
}
|
||||
}))
|
||||
|
||||
provider = SelectelProvider(123, 'secret_token')
|
||||
provider.populate(zone)
|
||||
|
||||
self.assertNotEqual(self.expected, zone.records)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_apply(self, fake_http):
|
||||
|
||||
fake_http.get('{}/unit.tests/records/'.format(self.API_URL),
|
||||
json=list())
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': '0'})
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
fake_http.post('{}/100000/records/'.format(self.API_URL), json=list())
|
||||
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
for record in self.expected:
|
||||
zone.add_record(record)
|
||||
|
||||
plan = provider.plan(zone)
|
||||
self.assertEquals(8, len(plan.changes))
|
||||
self.assertEquals(8, provider.apply(plan))
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_domain_list(self, fake_http):
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
|
||||
expected = {'unit.tests': self.domain[0]}
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
|
||||
result = provider.domain_list()
|
||||
self.assertEquals(result, expected)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_authentication_fail(self, fake_http):
|
||||
fake_http.get('{}/'.format(self.API_URL), status_code=401)
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
SelectelProvider(123, 'fail_token')
|
||||
self.assertEquals(text_type(ctx.exception),
|
||||
'Authorization failed. Invalid or empty token.')
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_not_exist_domain(self, fake_http):
|
||||
fake_http.get('{}/'.format(self.API_URL), status_code=404, json='')
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
|
||||
fake_http.post('{}/'.format(self.API_URL),
|
||||
json={"name": "unit.tests",
|
||||
"create_date": 1507154178,
|
||||
"id": 100000})
|
||||
fake_http.get('{}/unit.tests/records/'.format(self.API_URL),
|
||||
json=list())
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.api_record))})
|
||||
fake_http.post('{}/100000/records/'.format(self.API_URL),
|
||||
json=list())
|
||||
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
for record in self.expected:
|
||||
zone.add_record(record)
|
||||
|
||||
plan = provider.plan(zone)
|
||||
self.assertEquals(8, len(plan.changes))
|
||||
self.assertEquals(8, provider.apply(plan))
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_delete_no_exist_record(self, fake_http):
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.get('{}/100000/records/'.format(self.API_URL), json=list())
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': '0'})
|
||||
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
provider.delete_record('unit.tests', 'NS', zone)
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_change_record(self, fake_http):
|
||||
exist_record = [self.aaaa_record,
|
||||
{"content": "6.6.5.7",
|
||||
"ttl": 100,
|
||||
"type": "A",
|
||||
"id": 100001,
|
||||
"name": "delete.unit.tests"},
|
||||
{"content": "9.8.2.1",
|
||||
"ttl": 100,
|
||||
"type": "A",
|
||||
"id": 100002,
|
||||
"name": "unit.tests"}] # exist
|
||||
fake_http.get('{}/unit.tests/records/'.format(self.API_URL),
|
||||
json=exist_record)
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.get('{}/100000/records/'.format(self.API_URL),
|
||||
json=exist_record)
|
||||
fake_http.head('{}/unit.tests/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(exist_record))})
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
fake_http.head('{}/100000/records/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(exist_record))})
|
||||
fake_http.post('{}/100000/records/'.format(self.API_URL),
|
||||
json=list())
|
||||
fake_http.delete('{}/100000/records/100001'.format(self.API_URL),
|
||||
text="")
|
||||
fake_http.delete('{}/100000/records/100002'.format(self.API_URL),
|
||||
text="")
|
||||
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
for record in self.expected:
|
||||
zone.add_record(record)
|
||||
|
||||
plan = provider.plan(zone)
|
||||
self.assertEquals(8, len(plan.changes))
|
||||
self.assertEquals(8, provider.apply(plan))
|
||||
|
||||
@requests_mock.Mocker()
|
||||
def test_include_change_returns_false(self, fake_http):
|
||||
fake_http.get('{}/'.format(self.API_URL), json=self.domain)
|
||||
fake_http.head('{}/'.format(self.API_URL),
|
||||
headers={'X-Total-Count': str(len(self.domain))})
|
||||
provider = SelectelProvider(123, 'test_token')
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
exist_record = Record.new(zone, '', {
|
||||
'ttl': 60,
|
||||
'type': 'A',
|
||||
'values': ['1.1.1.1', '2.2.2.2']
|
||||
})
|
||||
new = Record.new(zone, '', {
|
||||
'ttl': 10,
|
||||
'type': 'A',
|
||||
'values': ['1.1.1.1', '2.2.2.2']
|
||||
})
|
||||
change = Update(exist_record, new)
|
||||
|
||||
include_change = provider._include_change(change)
|
||||
|
||||
self.assertFalse(include_change)
|
||||
@@ -0,0 +1,275 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from os.path import dirname, join
|
||||
from six import text_type
|
||||
|
||||
from suds import WebFault
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.provider.transip import TransipProvider
|
||||
from octodns.provider.yaml import YamlProvider
|
||||
from octodns.zone import Zone
|
||||
from transip.service.domain import DomainService
|
||||
from transip.service.objects import DnsEntry
|
||||
|
||||
|
||||
class MockFault(object):
|
||||
faultstring = ""
|
||||
faultcode = ""
|
||||
|
||||
def __init__(self, code, string, *args, **kwargs):
|
||||
self.faultstring = string
|
||||
self.faultcode = code
|
||||
|
||||
|
||||
class MockResponse(object):
|
||||
dnsEntries = []
|
||||
|
||||
|
||||
class MockDomainService(DomainService):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MockDomainService, self).__init__('MockDomainService', *args,
|
||||
**kwargs)
|
||||
self.mockupEntries = []
|
||||
|
||||
def mockup(self, records):
|
||||
|
||||
provider = TransipProvider('', '', '')
|
||||
|
||||
_dns_entries = []
|
||||
for record in records:
|
||||
if record._type in provider.SUPPORTS:
|
||||
entries_for = getattr(provider,
|
||||
'_entries_for_{}'.format(record._type))
|
||||
|
||||
# Root records have '@' as name
|
||||
name = record.name
|
||||
if name == '':
|
||||
name = provider.ROOT_RECORD
|
||||
|
||||
_dns_entries.extend(entries_for(name, record))
|
||||
|
||||
# NS is not supported as a DNS Entry,
|
||||
# so it should cover the if statement
|
||||
_dns_entries.append(
|
||||
DnsEntry('@', '3600', 'NS', 'ns01.transip.nl.'))
|
||||
|
||||
self.mockupEntries = _dns_entries
|
||||
|
||||
# Skips authentication layer and returns the entries loaded by "Mockup"
|
||||
def get_info(self, domain_name):
|
||||
|
||||
# Special 'domain' to trigger error
|
||||
if str(domain_name) == str('notfound.unit.tests'):
|
||||
self.raiseZoneNotFound()
|
||||
|
||||
result = MockResponse()
|
||||
result.dnsEntries = self.mockupEntries
|
||||
return result
|
||||
|
||||
def set_dns_entries(self, domain_name, dns_entries):
|
||||
|
||||
# Special 'domain' to trigger error
|
||||
if str(domain_name) == str('failsetdns.unit.tests'):
|
||||
self.raiseSaveError()
|
||||
|
||||
return True
|
||||
|
||||
def raiseZoneNotFound(self):
|
||||
fault = MockFault(str('102'), '102 is zone not found')
|
||||
document = {}
|
||||
raise WebFault(fault, document)
|
||||
|
||||
def raiseInvalidAuth(self):
|
||||
fault = MockFault(str('200'), '200 is invalid auth')
|
||||
document = {}
|
||||
raise WebFault(fault, document)
|
||||
|
||||
def raiseSaveError(self):
|
||||
fault = MockFault(str('200'), '202 random error')
|
||||
document = {}
|
||||
raise WebFault(fault, document)
|
||||
|
||||
|
||||
class TestTransipProvider(TestCase):
|
||||
bogus_key = str("""-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA0U5HGCkLrz423IyUf3u4cKN2WrNz1x5KNr6PvH2M/zxas+zB
|
||||
elbxkdT3AQ+wmfcIvOuTmFRTHv35q2um1aBrPxVw+2s+lWo28VwIRttwIB1vIeWu
|
||||
lSBnkEZQRLyPI2tH0i5QoMX4CVPf9rvij3Uslimi84jdzDfPFIh6jZ6C8nLipOTG
|
||||
0IMhge1ofVfB0oSy5H+7PYS2858QLAf5ruYbzbAxZRivS402wGmQ0d0Lc1KxraAj
|
||||
kiMM5yj/CkH/Vm2w9I6+tLFeASE4ub5HCP5G/ig4dbYtqZMQMpqyAbGxd5SOVtyn
|
||||
UHagAJUxf8DT3I8PyjEHjxdOPUsxNyRtepO/7QIDAQABAoIBAQC7fiZ7gxE/ezjD
|
||||
2n6PsHFpHVTBLS2gzzZl0dCKZeFvJk6ODJDImaeuHhrh7X8ifMNsEI9XjnojMhl8
|
||||
MGPzy88mZHugDNK0H8B19x5G8v1/Fz7dG5WHas660/HFkS+b59cfdXOugYiOOn9O
|
||||
08HBBpLZNRUOmVUuQfQTjapSwGLG8PocgpyRD4zx0LnldnJcqYCxwCdev+AAsPnq
|
||||
ibNtOd/MYD37w9MEGcaxLE8wGgkv8yd97aTjkgE+tp4zsM4QE4Rag133tsLLNznT
|
||||
4Qr/of15M3NW/DXq/fgctyRcJjZpU66eCXLCz2iRTnLyyxxDC2nwlxKbubV+lcS0
|
||||
S4hbfd/BAoGBAO8jXxEaiybR0aIhhSR5esEc3ymo8R8vBN3ZMJ+vr5jEPXr/ZuFj
|
||||
/R4cZ2XV3VoQJG0pvIOYVPZ5DpJM7W+zSXtJ/7bLXy4Bnmh/rc+YYgC+AXQoLSil
|
||||
iD2OuB2xAzRAK71DVSO0kv8gEEXCersPT2i6+vC2GIlJvLcYbOdRKWGxAoGBAOAQ
|
||||
aJbRLtKujH+kMdoMI7tRlL8XwI+SZf0FcieEu//nFyerTePUhVgEtcE+7eQ7hyhG
|
||||
fIXUFx/wALySoqFzdJDLc8U8pTLhbUaoLOTjkwnCTKQVprhnISqQqqh/0U5u47IE
|
||||
RWzWKN6OHb0CezNTq80Dr6HoxmPCnJHBHn5LinT9AoGAQSpvZpbIIqz8pmTiBl2A
|
||||
QQ2gFpcuFeRXPClKYcmbXVLkuhbNL1BzEniFCLAt4LQTaRf9ghLJ3FyCxwVlkpHV
|
||||
zV4N6/8hkcTpKOraL38D/dXJSaEFJVVuee/hZl3tVJjEEpA9rDwx7ooLRSdJEJ6M
|
||||
ciq55UyKBSdt4KssSiDI2RECgYBL3mJ7xuLy5bWfNsrGiVvD/rC+L928/5ZXIXPw
|
||||
26oI0Yfun7ulDH4GOroMcDF/GYT/Zzac3h7iapLlR0WYI47xxGI0A//wBZLJ3QIu
|
||||
krxkDo2C9e3Y/NqnHgsbOQR3aWbiDT4wxydZjIeXS3LKA2fl6Hyc90PN3cTEOb8I
|
||||
hq2gRQKBgEt0SxhhtyB93SjgTzmUZZ7PiEf0YJatfM6cevmjWHexrZH+x31PB72s
|
||||
fH2BQyTKKzoCLB1k/6HRaMnZdrWyWSZ7JKz3AHJ8+58d0Hr8LTrzDM1L6BbjeDct
|
||||
N4OiVz1I3rbZGYa396lpxO6ku8yCglisL1yrSP6DdEUp66ntpKVd
|
||||
-----END RSA PRIVATE KEY-----""")
|
||||
|
||||
def make_expected(self):
|
||||
expected = Zone('unit.tests.', [])
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
source.populate(expected)
|
||||
return expected
|
||||
|
||||
def test_init(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
TransipProvider('test', 'unittest')
|
||||
|
||||
self.assertEquals(
|
||||
str('Missing `key` of `key_file` parameter in config'),
|
||||
str(ctx.exception))
|
||||
|
||||
TransipProvider('test', 'unittest', key=self.bogus_key)
|
||||
|
||||
# Existence and content of the key is tested in the SDK on client call
|
||||
TransipProvider('test', 'unittest', key_file='/fake/path')
|
||||
|
||||
def test_populate(self):
|
||||
_expected = self.make_expected()
|
||||
|
||||
# Unhappy Plan - Not authenticated
|
||||
# Live test against API, will fail in an unauthorized error
|
||||
with self.assertRaises(WebFault) as ctx:
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone, True)
|
||||
|
||||
self.assertEquals(str('WebFault'),
|
||||
str(ctx.exception.__class__.__name__))
|
||||
|
||||
self.assertEquals(str('200'), ctx.exception.fault.faultcode)
|
||||
|
||||
# Unhappy Plan - Zone does not exists
|
||||
# Will trigger an exception if provider is used as a target for a
|
||||
# non-existing zone
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
zone = Zone('notfound.unit.tests.', [])
|
||||
provider.populate(zone, True)
|
||||
|
||||
self.assertEquals(str('TransipNewZoneException'),
|
||||
str(ctx.exception.__class__.__name__))
|
||||
|
||||
self.assertEquals(
|
||||
'populate: (102) Transip used as target' +
|
||||
' for non-existing zone: notfound.unit.tests.',
|
||||
text_type(ctx.exception))
|
||||
|
||||
# Happy Plan - Zone does not exists
|
||||
# Won't trigger an exception if provider is NOT used as a target for a
|
||||
# non-existing zone.
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
zone = Zone('notfound.unit.tests.', [])
|
||||
provider.populate(zone, False)
|
||||
|
||||
# Happy Plan - Populate with mockup records
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
provider._client.mockup(_expected.records)
|
||||
zone = Zone('unit.tests.', [])
|
||||
provider.populate(zone, False)
|
||||
|
||||
# Transip allows relative values for types like cname, mx.
|
||||
# Test is these are correctly appended with the domain
|
||||
provider._currentZone = zone
|
||||
self.assertEquals("www.unit.tests.", provider._parse_to_fqdn("www"))
|
||||
self.assertEquals("www.unit.tests.",
|
||||
provider._parse_to_fqdn("www.unit.tests."))
|
||||
self.assertEquals("www.sub.sub.sub.unit.tests.",
|
||||
provider._parse_to_fqdn("www.sub.sub.sub"))
|
||||
self.assertEquals("unit.tests.",
|
||||
provider._parse_to_fqdn("@"))
|
||||
|
||||
# Happy Plan - Even if the zone has no records the zone should exist
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
zone = Zone('unit.tests.', [])
|
||||
exists = provider.populate(zone, True)
|
||||
self.assertTrue(exists, 'populate should return true')
|
||||
|
||||
return
|
||||
|
||||
def test_plan(self):
|
||||
_expected = self.make_expected()
|
||||
|
||||
# Test Happy plan, only create
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
plan = provider.plan(_expected)
|
||||
|
||||
self.assertEqual(12, plan.change_counts['Create'])
|
||||
self.assertEqual(0, plan.change_counts['Update'])
|
||||
self.assertEqual(0, plan.change_counts['Delete'])
|
||||
|
||||
return
|
||||
|
||||
def test_apply(self):
|
||||
_expected = self.make_expected()
|
||||
|
||||
# Test happy flow. Create all supoorted records
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
plan = provider.plan(_expected)
|
||||
self.assertEqual(12, len(plan.changes))
|
||||
changes = provider.apply(plan)
|
||||
self.assertEqual(changes, len(plan.changes))
|
||||
|
||||
# Test unhappy flow. Trigger 'not found error' in apply stage
|
||||
# This should normally not happen as populate will capture it first
|
||||
# but just in case.
|
||||
changes = [] # reset changes
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
plan = provider.plan(_expected)
|
||||
plan.desired.name = 'notfound.unit.tests.'
|
||||
changes = provider.apply(plan)
|
||||
|
||||
# Changes should not be set due to an Exception
|
||||
self.assertEqual([], changes)
|
||||
|
||||
self.assertEquals(str('WebFault'),
|
||||
str(ctx.exception.__class__.__name__))
|
||||
|
||||
self.assertEquals(str('102'), ctx.exception.fault.faultcode)
|
||||
|
||||
# Test unhappy flow. Trigger a unrecoverable error while saving
|
||||
_expected = self.make_expected() # reset expected
|
||||
changes = [] # reset changes
|
||||
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
provider = TransipProvider('test', 'unittest', self.bogus_key)
|
||||
provider._client = MockDomainService('unittest', self.bogus_key)
|
||||
plan = provider.plan(_expected)
|
||||
plan.desired.name = 'failsetdns.unit.tests.'
|
||||
changes = provider.apply(plan)
|
||||
|
||||
# Changes should not be set due to an Exception
|
||||
self.assertEqual([], changes)
|
||||
|
||||
self.assertEquals(str('TransipException'),
|
||||
str(ctx.exception.__class__.__name__))
|
||||
@@ -8,6 +8,7 @@ from __future__ import absolute_import, division, print_function, \
|
||||
from os import makedirs
|
||||
from os.path import basename, dirname, isdir, isfile, join
|
||||
from unittest import TestCase
|
||||
from six import text_type
|
||||
from yaml import safe_load
|
||||
from yaml.constructor import ConstructorError
|
||||
|
||||
@@ -57,8 +58,8 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# We add everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEquals(15, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(15, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertFalse(isfile(yaml_file))
|
||||
|
||||
# Now actually do it
|
||||
@@ -67,8 +68,8 @@ class TestYamlProvider(TestCase):
|
||||
|
||||
# Dynamic plan
|
||||
plan = target.plan(dynamic_zone)
|
||||
self.assertEquals(5, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(5, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertFalse(isfile(dynamic_yaml_file))
|
||||
# Apply it
|
||||
self.assertEquals(5, target.apply(plan))
|
||||
@@ -79,16 +80,15 @@ class TestYamlProvider(TestCase):
|
||||
target.populate(reloaded)
|
||||
self.assertDictEqual(
|
||||
{'included': ['test']},
|
||||
filter(
|
||||
lambda x: x.name == 'included', reloaded.records
|
||||
)[0]._octodns)
|
||||
[x for x in reloaded.records
|
||||
if x.name == 'included'][0]._octodns)
|
||||
|
||||
self.assertFalse(zone.changes(reloaded, target=source))
|
||||
|
||||
# A 2nd sync should still create everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEquals(15, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(15, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
|
||||
with open(yaml_file) as fh:
|
||||
data = safe_load(fh.read())
|
||||
@@ -116,7 +116,7 @@ class TestYamlProvider(TestCase):
|
||||
self.assertTrue('value' in data.pop('www.sub'))
|
||||
|
||||
# make sure nothing is left
|
||||
self.assertEquals([], data.keys())
|
||||
self.assertEquals([], list(data.keys()))
|
||||
|
||||
with open(dynamic_yaml_file) as fh:
|
||||
data = safe_load(fh.read())
|
||||
@@ -145,7 +145,7 @@ class TestYamlProvider(TestCase):
|
||||
# self.assertTrue('dynamic' in dyna)
|
||||
|
||||
# make sure nothing is left
|
||||
self.assertEquals([], data.keys())
|
||||
self.assertEquals([], list(data.keys()))
|
||||
|
||||
def test_empty(self):
|
||||
source = YamlProvider('test', join(dirname(__file__), 'config'))
|
||||
@@ -178,7 +178,7 @@ class TestYamlProvider(TestCase):
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
source.populate(zone)
|
||||
self.assertEquals('Record www.sub.unit.tests. is under a managed '
|
||||
'subzone', ctx.exception.message)
|
||||
'subzone', text_type(ctx.exception))
|
||||
|
||||
|
||||
class TestSplitYamlProvider(TestCase):
|
||||
@@ -201,9 +201,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# This isn't great, but given the variable nature of the temp dir
|
||||
# names, it's necessary.
|
||||
self.assertItemsEqual(
|
||||
yaml_files,
|
||||
(basename(f) for f in _list_all_yaml_files(directory)))
|
||||
d = list(basename(f) for f in _list_all_yaml_files(directory))
|
||||
self.assertEqual(len(yaml_files), len(d))
|
||||
|
||||
def test_zone_directory(self):
|
||||
source = SplitYamlProvider(
|
||||
@@ -252,8 +251,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# We add everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEquals(15, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(15, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertFalse(isdir(zone_dir))
|
||||
|
||||
# Now actually do it
|
||||
@@ -261,8 +260,8 @@ class TestSplitYamlProvider(TestCase):
|
||||
|
||||
# Dynamic plan
|
||||
plan = target.plan(dynamic_zone)
|
||||
self.assertEquals(5, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(5, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
self.assertFalse(isdir(dynamic_zone_dir))
|
||||
# Apply it
|
||||
self.assertEquals(5, target.apply(plan))
|
||||
@@ -273,16 +272,15 @@ class TestSplitYamlProvider(TestCase):
|
||||
target.populate(reloaded)
|
||||
self.assertDictEqual(
|
||||
{'included': ['test']},
|
||||
filter(
|
||||
lambda x: x.name == 'included', reloaded.records
|
||||
)[0]._octodns)
|
||||
[x for x in reloaded.records
|
||||
if x.name == 'included'][0]._octodns)
|
||||
|
||||
self.assertFalse(zone.changes(reloaded, target=source))
|
||||
|
||||
# A 2nd sync should still create everything
|
||||
plan = target.plan(zone)
|
||||
self.assertEquals(15, len(filter(lambda c: isinstance(c, Create),
|
||||
plan.changes)))
|
||||
self.assertEquals(15, len([c for c in plan.changes
|
||||
if isinstance(c, Create)]))
|
||||
|
||||
yaml_file = join(zone_dir, '$unit.tests.yaml')
|
||||
self.assertTrue(isfile(yaml_file))
|
||||
@@ -371,7 +369,7 @@ class TestSplitYamlProvider(TestCase):
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
source.populate(zone)
|
||||
self.assertEquals('Record www.sub.unit.tests. is under a managed '
|
||||
'subzone', ctx.exception.message)
|
||||
'subzone', text_type(ctx.exception))
|
||||
|
||||
|
||||
class TestOverridingYamlProvider(TestCase):
|
||||
|
||||
+424
-35
@@ -5,12 +5,14 @@
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.record import ARecord, AaaaRecord, AliasRecord, CaaRecord, \
|
||||
CnameRecord, Create, Delete, GeoValue, MxRecord, NaptrRecord, NaptrValue, \
|
||||
NsRecord, PtrRecord, Record, SshfpRecord, SpfRecord, SrvRecord, \
|
||||
TxtRecord, Update, ValidationError, _Dynamic, _DynamicPool, _DynamicRule
|
||||
CaaValue, CnameRecord, Create, Delete, GeoValue, MxRecord, MxValue, \
|
||||
NaptrRecord, NaptrValue, NsRecord, PtrRecord, Record, SshfpRecord, \
|
||||
SshfpValue, SpfRecord, SrvRecord, SrvValue, TxtRecord, Update, \
|
||||
ValidationError, _Dynamic, _DynamicPool, _DynamicRule
|
||||
from octodns.zone import Zone
|
||||
|
||||
from helpers import DynamicProvider, GeoProvider, SimpleProvider
|
||||
@@ -482,113 +484,140 @@ class TestRecord(TestCase):
|
||||
# full sorting
|
||||
# equivalent
|
||||
b_naptr_value = b.values[0]
|
||||
self.assertEquals(0, b_naptr_value.__cmp__(b_naptr_value))
|
||||
self.assertTrue(b_naptr_value == b_naptr_value)
|
||||
self.assertFalse(b_naptr_value != b_naptr_value)
|
||||
self.assertTrue(b_naptr_value <= b_naptr_value)
|
||||
self.assertTrue(b_naptr_value >= b_naptr_value)
|
||||
# by order
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 10,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 40,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
}))
|
||||
# by preference
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 10,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 40,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
}))
|
||||
# by flags
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'A',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'Z',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
}))
|
||||
# by service
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'A',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'Z',
|
||||
'regexp': 'O',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
}))
|
||||
# by regexp
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'A',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'Z',
|
||||
'replacement': 'x',
|
||||
})))
|
||||
}))
|
||||
# by replacement
|
||||
self.assertEquals(1, b_naptr_value.__cmp__(NaptrValue({
|
||||
self.assertTrue(b_naptr_value > NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'a',
|
||||
})))
|
||||
self.assertEquals(-1, b_naptr_value.__cmp__(NaptrValue({
|
||||
}))
|
||||
self.assertTrue(b_naptr_value < NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'z',
|
||||
})))
|
||||
}))
|
||||
|
||||
# __repr__ doesn't blow up
|
||||
a.__repr__()
|
||||
|
||||
# Hash
|
||||
v = NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 31,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'z',
|
||||
})
|
||||
o = NaptrValue({
|
||||
'order': 30,
|
||||
'preference': 32,
|
||||
'flags': 'M',
|
||||
'service': 'N',
|
||||
'regexp': 'O',
|
||||
'replacement': 'z',
|
||||
})
|
||||
values = set()
|
||||
values.add(v)
|
||||
self.assertTrue(v in values)
|
||||
self.assertFalse(o in values)
|
||||
values.add(o)
|
||||
self.assertTrue(o in values)
|
||||
|
||||
def test_ns(self):
|
||||
a_values = ['5.6.7.8.', '6.7.8.9.', '7.8.9.0.']
|
||||
a_data = {'ttl': 30, 'values': a_values}
|
||||
@@ -758,14 +787,14 @@ class TestRecord(TestCase):
|
||||
# Missing type
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Record.new(self.zone, 'unknown', {})
|
||||
self.assertTrue('missing type' in ctx.exception.message)
|
||||
self.assertTrue('missing type' in text_type(ctx.exception))
|
||||
|
||||
# Unknown type
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Record.new(self.zone, 'unknown', {
|
||||
'type': 'XXX',
|
||||
})
|
||||
self.assertTrue('Unknown record type' in ctx.exception.message)
|
||||
self.assertTrue('Unknown record type' in text_type(ctx.exception))
|
||||
|
||||
def test_change(self):
|
||||
existing = Record.new(self.zone, 'txt', {
|
||||
@@ -796,6 +825,38 @@ class TestRecord(TestCase):
|
||||
self.assertEquals(values, geo.values)
|
||||
self.assertEquals(['NA-US', 'NA'], list(geo.parents))
|
||||
|
||||
a = GeoValue('NA-US-CA', values)
|
||||
b = GeoValue('AP-JP', values)
|
||||
c = GeoValue('NA-US-CA', ['2.3.4.5'])
|
||||
|
||||
self.assertEqual(a, a)
|
||||
self.assertEqual(b, b)
|
||||
self.assertEqual(c, c)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
|
||||
self.assertTrue(a > b)
|
||||
self.assertTrue(a < c)
|
||||
self.assertTrue(b < a)
|
||||
self.assertTrue(b < c)
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c > b)
|
||||
|
||||
self.assertTrue(a >= a)
|
||||
self.assertTrue(a >= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(b <= a)
|
||||
self.assertTrue(b <= b)
|
||||
self.assertTrue(b <= c)
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c > b)
|
||||
self.assertTrue(c >= b)
|
||||
|
||||
def test_healthcheck(self):
|
||||
new = Record.new(self.zone, 'a', {
|
||||
'ttl': 44,
|
||||
@@ -851,11 +912,339 @@ class TestRecord(TestCase):
|
||||
})
|
||||
self.assertFalse(new.ignored)
|
||||
|
||||
def test_ordering_functions(self):
|
||||
a = Record.new(self.zone, 'a', {
|
||||
'ttl': 44,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
b = Record.new(self.zone, 'b', {
|
||||
'ttl': 44,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
c = Record.new(self.zone, 'c', {
|
||||
'ttl': 44,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
aaaa = Record.new(self.zone, 'a', {
|
||||
'ttl': 44,
|
||||
'type': 'AAAA',
|
||||
'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
|
||||
})
|
||||
|
||||
self.assertEquals(a, a)
|
||||
self.assertEquals(b, b)
|
||||
self.assertEquals(c, c)
|
||||
self.assertEquals(aaaa, aaaa)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(a, aaaa)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(b, aaaa)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
self.assertNotEqual(c, aaaa)
|
||||
self.assertNotEqual(aaaa, a)
|
||||
self.assertNotEqual(aaaa, b)
|
||||
self.assertNotEqual(aaaa, c)
|
||||
|
||||
self.assertTrue(a < b)
|
||||
self.assertTrue(a < c)
|
||||
self.assertTrue(a < aaaa)
|
||||
self.assertTrue(b > a)
|
||||
self.assertTrue(b < c)
|
||||
self.assertTrue(b > aaaa)
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c > b)
|
||||
self.assertTrue(c > aaaa)
|
||||
self.assertTrue(aaaa > a)
|
||||
self.assertTrue(aaaa < b)
|
||||
self.assertTrue(aaaa < c)
|
||||
|
||||
self.assertTrue(a <= a)
|
||||
self.assertTrue(a <= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(a <= aaaa)
|
||||
self.assertTrue(b >= a)
|
||||
self.assertTrue(b >= b)
|
||||
self.assertTrue(b <= c)
|
||||
self.assertTrue(b >= aaaa)
|
||||
self.assertTrue(c >= a)
|
||||
self.assertTrue(c >= b)
|
||||
self.assertTrue(c >= c)
|
||||
self.assertTrue(c >= aaaa)
|
||||
self.assertTrue(aaaa >= a)
|
||||
self.assertTrue(aaaa <= b)
|
||||
self.assertTrue(aaaa <= c)
|
||||
self.assertTrue(aaaa <= aaaa)
|
||||
|
||||
def test_caa_value(self):
|
||||
a = CaaValue({'flags': 0, 'tag': 'a', 'value': 'v'})
|
||||
b = CaaValue({'flags': 1, 'tag': 'a', 'value': 'v'})
|
||||
c = CaaValue({'flags': 0, 'tag': 'c', 'value': 'v'})
|
||||
d = CaaValue({'flags': 0, 'tag': 'a', 'value': 'z'})
|
||||
|
||||
self.assertEqual(a, a)
|
||||
self.assertEqual(b, b)
|
||||
self.assertEqual(c, c)
|
||||
self.assertEqual(d, d)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(a, d)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(b, d)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
self.assertNotEqual(c, d)
|
||||
|
||||
self.assertTrue(a < b)
|
||||
self.assertTrue(a < c)
|
||||
self.assertTrue(a < d)
|
||||
|
||||
self.assertTrue(b > a)
|
||||
self.assertTrue(b > c)
|
||||
self.assertTrue(b > d)
|
||||
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c < b)
|
||||
self.assertTrue(c > d)
|
||||
|
||||
self.assertTrue(d > a)
|
||||
self.assertTrue(d < b)
|
||||
self.assertTrue(d < c)
|
||||
|
||||
self.assertTrue(a <= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(a <= d)
|
||||
self.assertTrue(a <= a)
|
||||
self.assertTrue(a >= a)
|
||||
|
||||
self.assertTrue(b >= a)
|
||||
self.assertTrue(b >= c)
|
||||
self.assertTrue(b >= d)
|
||||
self.assertTrue(b >= b)
|
||||
self.assertTrue(b <= b)
|
||||
|
||||
self.assertTrue(c >= a)
|
||||
self.assertTrue(c <= b)
|
||||
self.assertTrue(c >= d)
|
||||
self.assertTrue(c >= c)
|
||||
self.assertTrue(c <= c)
|
||||
|
||||
self.assertTrue(d >= a)
|
||||
self.assertTrue(d <= b)
|
||||
self.assertTrue(d <= c)
|
||||
self.assertTrue(d >= d)
|
||||
self.assertTrue(d <= d)
|
||||
|
||||
def test_mx_value(self):
|
||||
a = MxValue({'preference': 0, 'priority': 'a', 'exchange': 'v',
|
||||
'value': '1'})
|
||||
b = MxValue({'preference': 10, 'priority': 'a', 'exchange': 'v',
|
||||
'value': '2'})
|
||||
c = MxValue({'preference': 0, 'priority': 'b', 'exchange': 'z',
|
||||
'value': '3'})
|
||||
|
||||
self.assertEqual(a, a)
|
||||
self.assertEqual(b, b)
|
||||
self.assertEqual(c, c)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
|
||||
self.assertTrue(a < b)
|
||||
self.assertTrue(a < c)
|
||||
|
||||
self.assertTrue(b > a)
|
||||
self.assertTrue(b > c)
|
||||
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c < b)
|
||||
|
||||
self.assertTrue(a <= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(a <= a)
|
||||
self.assertTrue(a >= a)
|
||||
|
||||
self.assertTrue(b >= a)
|
||||
self.assertTrue(b >= c)
|
||||
self.assertTrue(b >= b)
|
||||
self.assertTrue(b <= b)
|
||||
|
||||
self.assertTrue(c >= a)
|
||||
self.assertTrue(c <= b)
|
||||
self.assertTrue(c >= c)
|
||||
self.assertTrue(c <= c)
|
||||
|
||||
def test_sshfp_value(self):
|
||||
a = SshfpValue({'algorithm': 0, 'fingerprint_type': 0,
|
||||
'fingerprint': 'abcd'})
|
||||
b = SshfpValue({'algorithm': 1, 'fingerprint_type': 0,
|
||||
'fingerprint': 'abcd'})
|
||||
c = SshfpValue({'algorithm': 0, 'fingerprint_type': 1,
|
||||
'fingerprint': 'abcd'})
|
||||
d = SshfpValue({'algorithm': 0, 'fingerprint_type': 0,
|
||||
'fingerprint': 'bcde'})
|
||||
|
||||
self.assertEqual(a, a)
|
||||
self.assertEqual(b, b)
|
||||
self.assertEqual(c, c)
|
||||
self.assertEqual(d, d)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(a, d)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(b, d)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
self.assertNotEqual(c, d)
|
||||
self.assertNotEqual(d, a)
|
||||
self.assertNotEqual(d, b)
|
||||
self.assertNotEqual(d, c)
|
||||
|
||||
self.assertTrue(a < b)
|
||||
self.assertTrue(a < c)
|
||||
|
||||
self.assertTrue(b > a)
|
||||
self.assertTrue(b > c)
|
||||
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c < b)
|
||||
|
||||
self.assertTrue(a <= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(a <= a)
|
||||
self.assertTrue(a >= a)
|
||||
|
||||
self.assertTrue(b >= a)
|
||||
self.assertTrue(b >= c)
|
||||
self.assertTrue(b >= b)
|
||||
self.assertTrue(b <= b)
|
||||
|
||||
self.assertTrue(c >= a)
|
||||
self.assertTrue(c <= b)
|
||||
self.assertTrue(c >= c)
|
||||
self.assertTrue(c <= c)
|
||||
|
||||
# Hash
|
||||
values = set()
|
||||
values.add(a)
|
||||
self.assertTrue(a in values)
|
||||
self.assertFalse(b in values)
|
||||
values.add(b)
|
||||
self.assertTrue(b in values)
|
||||
|
||||
def test_srv_value(self):
|
||||
a = SrvValue({'priority': 0, 'weight': 0, 'port': 0, 'target': 'foo.'})
|
||||
b = SrvValue({'priority': 1, 'weight': 0, 'port': 0, 'target': 'foo.'})
|
||||
c = SrvValue({'priority': 0, 'weight': 2, 'port': 0, 'target': 'foo.'})
|
||||
d = SrvValue({'priority': 0, 'weight': 0, 'port': 3, 'target': 'foo.'})
|
||||
e = SrvValue({'priority': 0, 'weight': 0, 'port': 0, 'target': 'mmm.'})
|
||||
|
||||
self.assertEqual(a, a)
|
||||
self.assertEqual(b, b)
|
||||
self.assertEqual(c, c)
|
||||
self.assertEqual(d, d)
|
||||
self.assertEqual(e, e)
|
||||
|
||||
self.assertNotEqual(a, b)
|
||||
self.assertNotEqual(a, c)
|
||||
self.assertNotEqual(a, d)
|
||||
self.assertNotEqual(a, e)
|
||||
self.assertNotEqual(b, a)
|
||||
self.assertNotEqual(b, c)
|
||||
self.assertNotEqual(b, d)
|
||||
self.assertNotEqual(b, e)
|
||||
self.assertNotEqual(c, a)
|
||||
self.assertNotEqual(c, b)
|
||||
self.assertNotEqual(c, d)
|
||||
self.assertNotEqual(c, e)
|
||||
self.assertNotEqual(d, a)
|
||||
self.assertNotEqual(d, b)
|
||||
self.assertNotEqual(d, c)
|
||||
self.assertNotEqual(d, e)
|
||||
self.assertNotEqual(e, a)
|
||||
self.assertNotEqual(e, b)
|
||||
self.assertNotEqual(e, c)
|
||||
self.assertNotEqual(e, d)
|
||||
|
||||
self.assertTrue(a < b)
|
||||
self.assertTrue(a < c)
|
||||
|
||||
self.assertTrue(b > a)
|
||||
self.assertTrue(b > c)
|
||||
|
||||
self.assertTrue(c > a)
|
||||
self.assertTrue(c < b)
|
||||
|
||||
self.assertTrue(a <= b)
|
||||
self.assertTrue(a <= c)
|
||||
self.assertTrue(a <= a)
|
||||
self.assertTrue(a >= a)
|
||||
|
||||
self.assertTrue(b >= a)
|
||||
self.assertTrue(b >= c)
|
||||
self.assertTrue(b >= b)
|
||||
self.assertTrue(b <= b)
|
||||
|
||||
self.assertTrue(c >= a)
|
||||
self.assertTrue(c <= b)
|
||||
self.assertTrue(c >= c)
|
||||
self.assertTrue(c <= c)
|
||||
|
||||
# Hash
|
||||
values = set()
|
||||
values.add(a)
|
||||
self.assertTrue(a in values)
|
||||
self.assertFalse(b in values)
|
||||
values.add(b)
|
||||
self.assertTrue(b in values)
|
||||
|
||||
|
||||
class TestRecordValidation(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
|
||||
def test_base(self):
|
||||
# fqdn length, DNS defins max as 253
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
# The . will put this over the edge
|
||||
name = 'x' * (253 - len(self.zone.name))
|
||||
Record.new(self.zone, name, {
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
reason = ctx.exception.reasons[0]
|
||||
self.assertTrue(reason.startswith('invalid fqdn, "xxxx'))
|
||||
self.assertTrue(reason.endswith('.unit.tests." is too long at 254'
|
||||
' chars, max is 253'))
|
||||
|
||||
# label length, DNS defins max as 63
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
# The . will put this over the edge
|
||||
name = 'x' * 64
|
||||
Record.new(self.zone, name, {
|
||||
'ttl': 300,
|
||||
'type': 'A',
|
||||
'value': '1.2.3.4',
|
||||
})
|
||||
reason = ctx.exception.reasons[0]
|
||||
self.assertTrue(reason.startswith('invalid name, "xxxx'))
|
||||
self.assertTrue(reason.endswith('xxx" is too long at 64'
|
||||
' chars, max is 63'))
|
||||
|
||||
# no ttl
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, '', {
|
||||
@@ -2460,7 +2849,7 @@ class TestDynamicRecords(TestCase):
|
||||
'weight': 1,
|
||||
'value': '6.6.6.6',
|
||||
}, {
|
||||
'weight': 256,
|
||||
'weight': 16,
|
||||
'value': '7.7.7.7',
|
||||
}],
|
||||
},
|
||||
@@ -2484,7 +2873,7 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(['invalid weight "256" in pool "three" value 2'],
|
||||
self.assertEquals(['invalid weight "16" in pool "three" value 2'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# invalid non-int weight
|
||||
@@ -2845,7 +3234,7 @@ class TestDynamicRecords(TestCase):
|
||||
self.assertEquals(['rule 1 invalid pool "[]"'],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# rule references non-existant pool
|
||||
# rule references non-existent pool
|
||||
a_data = {
|
||||
'dynamic': {
|
||||
'pools': {
|
||||
@@ -2864,7 +3253,7 @@ class TestDynamicRecords(TestCase):
|
||||
},
|
||||
'rules': [{
|
||||
'geos': ['NA-US-CA'],
|
||||
'pool': 'non-existant',
|
||||
'pool': 'non-existent',
|
||||
}, {
|
||||
'pool': 'one',
|
||||
}],
|
||||
@@ -2878,7 +3267,7 @@ class TestDynamicRecords(TestCase):
|
||||
}
|
||||
with self.assertRaises(ValidationError) as ctx:
|
||||
Record.new(self.zone, 'bad', a_data)
|
||||
self.assertEquals(["rule 1 undefined pool \"non-existant\""],
|
||||
self.assertEquals(["rule 1 undefined pool \"non-existent\""],
|
||||
ctx.exception.reasons)
|
||||
|
||||
# rule with invalid geos
|
||||
|
||||
@@ -9,6 +9,7 @@ import dns.zone
|
||||
from dns.exception import DNSException
|
||||
|
||||
from mock import patch
|
||||
from six import text_type
|
||||
from unittest import TestCase
|
||||
|
||||
from octodns.source.axfr import AxfrSource, AxfrSourceZoneTransferFailed, \
|
||||
@@ -38,7 +39,7 @@ class TestAxfrSource(TestCase):
|
||||
zone = Zone('unit.tests.', [])
|
||||
self.source.populate(zone)
|
||||
self.assertEquals('Unable to Perform Zone Transfer',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
|
||||
class TestZoneFileSource(TestCase):
|
||||
@@ -68,4 +69,4 @@ class TestZoneFileSource(TestCase):
|
||||
zone = Zone('invalid.zone.', [])
|
||||
self.source.populate(zone)
|
||||
self.assertEquals('The DNS zone has no NS RRset at its origin.',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
from unittest import TestCase
|
||||
from yaml.constructor import ConstructorError
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from __future__ import absolute_import, division, print_function, \
|
||||
unicode_literals
|
||||
|
||||
from unittest import TestCase
|
||||
from six import text_type
|
||||
|
||||
from octodns.record import ARecord, AaaaRecord, Create, Delete, Record, Update
|
||||
from octodns.zone import DuplicateRecordException, InvalidNodeException, \
|
||||
@@ -47,7 +48,7 @@ class TestZone(TestCase):
|
||||
with self.assertRaises(DuplicateRecordException) as ctx:
|
||||
zone.add_record(a)
|
||||
self.assertEquals('Duplicate record a.unit.tests., type A',
|
||||
ctx.exception.message)
|
||||
text_type(ctx.exception))
|
||||
self.assertEquals(zone.records, set([a]))
|
||||
|
||||
# can add duplicate with replace=True
|
||||
@@ -137,7 +138,7 @@ class TestZone(TestCase):
|
||||
def test_missing_dot(self):
|
||||
with self.assertRaises(Exception) as ctx:
|
||||
Zone('not.allowed', [])
|
||||
self.assertTrue('missing ending dot' in ctx.exception.message)
|
||||
self.assertTrue('missing ending dot' in text_type(ctx.exception))
|
||||
|
||||
def test_sub_zones(self):
|
||||
|
||||
@@ -160,7 +161,7 @@ class TestZone(TestCase):
|
||||
})
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('not of type NS', ctx.exception.message)
|
||||
self.assertTrue('not of type NS', text_type(ctx.exception))
|
||||
# Can add it w/lenient
|
||||
zone.add_record(record, lenient=True)
|
||||
self.assertEquals(set([record]), zone.records)
|
||||
@@ -174,7 +175,7 @@ class TestZone(TestCase):
|
||||
})
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('under a managed sub-zone', ctx.exception.message)
|
||||
self.assertTrue('under a managed sub-zone', text_type(ctx.exception))
|
||||
# Can add it w/lenient
|
||||
zone.add_record(record, lenient=True)
|
||||
self.assertEquals(set([record]), zone.records)
|
||||
@@ -188,7 +189,7 @@ class TestZone(TestCase):
|
||||
})
|
||||
with self.assertRaises(SubzoneRecordException) as ctx:
|
||||
zone.add_record(record)
|
||||
self.assertTrue('under a managed sub-zone', ctx.exception.message)
|
||||
self.assertTrue('under a managed sub-zone', text_type(ctx.exception))
|
||||
# Can add it w/lenient
|
||||
zone.add_record(record, lenient=True)
|
||||
self.assertEquals(set([record]), zone.records)
|
||||
|
||||
Reference in New Issue
Block a user