octodns-cloudns 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,17 +1,32 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: octodns-cloudns
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: ClouDNS API provider for octoDNS
5
5
  Home-page: https://github.com/octodns/octodns-cloudns
6
6
  Author: ClouDNS
7
7
  Author-email: support@cloudns.net
8
8
  License: MIT
9
- Platform: UNKNOWN
10
9
  Requires-Python: >=3.6
11
10
  Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: octodns>=0.9.17
13
+ Requires-Dist: requests>=2.27.0
12
14
  Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: pytest-cov; extra == "dev"
17
+ Requires-Dist: pytest-network; extra == "dev"
18
+ Requires-Dist: requests_mock; extra == "dev"
19
+ Requires-Dist: black<24.0.0,>=23.1.0; extra == "dev"
20
+ Requires-Dist: build>=0.7.0; extra == "dev"
21
+ Requires-Dist: isort>=5.11.5; extra == "dev"
22
+ Requires-Dist: pyflakes>=2.2.0; extra == "dev"
23
+ Requires-Dist: readme_renderer[md]>=26.0; extra == "dev"
24
+ Requires-Dist: twine>=3.4.2; extra == "dev"
13
25
  Provides-Extra: test
14
- License-File: LICENSE
26
+ Requires-Dist: pytest; extra == "test"
27
+ Requires-Dist: pytest-cov; extra == "test"
28
+ Requires-Dist: pytest-network; extra == "test"
29
+ Requires-Dist: requests_mock; extra == "test"
15
30
 
16
31
  ## ClouDNS API provider for octoDNS
17
32
 
@@ -64,5 +79,3 @@ ClouDNSProvider does not support dynamic records.
64
79
  ### Development
65
80
 
66
81
  See the [/script/](/script/) directory for some tools to help with the development process. They generally follow the [Script to rule them all](https://github.com/github/scripts-to-rule-them-all) pattern. Most useful is `./script/bootstrap` which will create a venv and install both the runtime and development related requirements. It will also hook up a pre-commit hook that covers most of what's run by CI.
67
-
68
-
@@ -8,7 +8,7 @@ from octodns.record import Record
8
8
  logging.basicConfig(level=logging.DEBUG)
9
9
  logger = logging.getLogger(__name__)
10
10
 
11
- __version__ = __VERSION__ = '0.0.4'
11
+ __version__ = __VERSION__ = '0.0.6'
12
12
 
13
13
  class ClouDNSClientException(ProviderException):
14
14
  pass
@@ -580,75 +580,89 @@ class ClouDNSProvider(BaseProvider):
580
580
  zone = existing.zone
581
581
  record_ids = []
582
582
  for record_id, record in self.zone_records(zone).items():
583
- for value in existing.values:
584
- if existing._type == 'NAPTR' and record['type'] == 'NAPTR':
585
- if (
586
- existing.name == record['host']
587
- and value.order == int(record['order'])
588
- and value.preference == int(record['pref'])
589
- and value.flags == record['flag']
590
- ):
591
- record_ids.append(record_id)
583
+ if existing._type == 'NAPTR' and record['type'] == 'NAPTR':
584
+ for value in existing.values:
585
+ if (
586
+ existing.name == record['host']
587
+ and value.order == int(record['order'])
588
+ and value.preference == int(record['pref'])
589
+ and value.flags == record['flag']
590
+ ):
591
+ record_ids.append(record_id)
592
592
  elif existing._type == 'SSHFP' and record['type'] == 'SSHFP':
593
- if (
594
- existing.name == record['host']
595
- and value.fingerprint_type == int(record['fp_type'])
596
- and value.algorithm == int(record['algorithm'])
597
- and value.fingerprint == record['record']
598
- ):
599
- record_ids.append(record_id)
593
+ for value in existing.values:
594
+ if (
595
+ existing.name == record['host']
596
+ and value.fingerprint_type == int(record['fp_type'])
597
+ and value.algorithm == int(record['algorithm'])
598
+ and value.fingerprint == record['record']
599
+ ):
600
+ record_ids.append(record_id)
600
601
  elif existing._type == 'SRV' and record['type'] == 'SRV':
601
- if (
602
- existing.name == record['host']
603
- and value.priority == int(record['priority'])
604
- and value.weight == int(record['weight'])
605
- and value.port == record['port']
606
- and value.target == record['record']
607
- ):
608
- record_ids.append(record_id)
602
+ for value in existing.values:
603
+ if (
604
+ existing.name == record['host']
605
+ and value.priority == int(record['priority'])
606
+ and value.weight == int(record['weight'])
607
+ and value.port == record['port']
608
+ and value.target == record['record']
609
+ ):
610
+ record_ids.append(record_id)
609
611
  elif existing._type == 'CAA' and record['type'] == 'CAA':
610
- if (
611
- existing.name == record['host']
612
- and value.flags == record['caa_flag']
613
- and value.tag == record['caa_type']
614
- and value.value == record['caa_value']
615
- ):
616
- record_ids.append(record_id)
612
+ for value in existing.values:
613
+ if (
614
+ existing.name == record['host']
615
+ and value.flags == record['caa_flag']
616
+ and value.tag == record['caa_type']
617
+ and value.value == record['caa_value']
618
+ ):
619
+ record_ids.append(record_id)
617
620
  elif existing._type == 'MX' and record['type'] == 'MX':
618
- if (
619
- existing.name == record['host']
620
- and value.preference == int(record['priority'])
621
- and value.exchange == record['record']
622
- ):
623
- record_ids.append(record_id)
621
+ for value in existing.values:
622
+ if (
623
+ existing.name == record['host']
624
+ and value.preference == int(record['priority'])
625
+ and value.exchange == record['record']
626
+ ):
627
+ record_ids.append(record_id)
624
628
 
625
629
  elif existing._type == 'LOC' and record['type'] == 'LOC':
626
- if (
627
- existing.name == record['host']
628
- and value.lat_degrees == record['lat_deg']
629
- and value.lat_minutes == record['lat_min']
630
- and value.lat_seconds == record['lat_sec']
631
- and value.lat_direction == record['lat_dir']
632
- and value.long_degrees == record['long_deg']
633
- and value.long_minutes == record['long_min']
634
- and value.long_seconds == record['long_sec']
635
- and value.long_direction == record['long_dir']
636
- and value.altitude == record['altitude']
637
- and value.size == record['size']
638
- and value.precision_horz == record['h_precision']
639
- and value.precision_vert == record['v_precision']
640
- ):
641
- record_ids.append(record_id)
630
+ for value in existing.values:
631
+ if (
632
+ existing.name == record['host']
633
+ and value.lat_degrees == record['lat_deg']
634
+ and value.lat_minutes == record['lat_min']
635
+ and value.lat_seconds == record['lat_sec']
636
+ and value.lat_direction == record['lat_dir']
637
+ and value.long_degrees == record['long_deg']
638
+ and value.long_minutes == record['long_min']
639
+ and value.long_seconds == record['long_sec']
640
+ and value.long_direction == record['long_dir']
641
+ and value.altitude == record['altitude']
642
+ and value.size == record['size']
643
+ and value.precision_horz == record['h_precision']
644
+ and value.precision_vert == record['v_precision']
645
+ ):
646
+ record_ids.append(record_id)
642
647
  else:
643
648
  if (record == 'Failed' or record == 'Missing domain-name'):
644
649
  continue
645
- if (
646
- existing.name == record['host']
647
- and existing._type == record['type']
648
- and value == record['record']
649
- ):
650
- print(record['record'])
651
- record_ids.append(record_id)
650
+
651
+ if hasattr(existing, 'value'):
652
+ if (
653
+ existing.name == record['host']
654
+ and existing._type == record['type']
655
+ and existing.value == record['record']
656
+ ):
657
+ record_ids.append(record_id)
658
+ elif hasattr(existing, 'values'):
659
+ for value in existing.values:
660
+ if (
661
+ existing.name == record['host']
662
+ and existing._type == record['type']
663
+ and value == record['record']
664
+ ):
665
+ record_ids.append(record_id)
652
666
  return record_ids
653
667
 
654
668
 
@@ -1,17 +1,32 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: octodns-cloudns
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: ClouDNS API provider for octoDNS
5
5
  Home-page: https://github.com/octodns/octodns-cloudns
6
6
  Author: ClouDNS
7
7
  Author-email: support@cloudns.net
8
8
  License: MIT
9
- Platform: UNKNOWN
10
9
  Requires-Python: >=3.6
11
10
  Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: octodns>=0.9.17
13
+ Requires-Dist: requests>=2.27.0
12
14
  Provides-Extra: dev
15
+ Requires-Dist: pytest; extra == "dev"
16
+ Requires-Dist: pytest-cov; extra == "dev"
17
+ Requires-Dist: pytest-network; extra == "dev"
18
+ Requires-Dist: requests_mock; extra == "dev"
19
+ Requires-Dist: black<24.0.0,>=23.1.0; extra == "dev"
20
+ Requires-Dist: build>=0.7.0; extra == "dev"
21
+ Requires-Dist: isort>=5.11.5; extra == "dev"
22
+ Requires-Dist: pyflakes>=2.2.0; extra == "dev"
23
+ Requires-Dist: readme_renderer[md]>=26.0; extra == "dev"
24
+ Requires-Dist: twine>=3.4.2; extra == "dev"
13
25
  Provides-Extra: test
14
- License-File: LICENSE
26
+ Requires-Dist: pytest; extra == "test"
27
+ Requires-Dist: pytest-cov; extra == "test"
28
+ Requires-Dist: pytest-network; extra == "test"
29
+ Requires-Dist: requests_mock; extra == "test"
15
30
 
16
31
  ## ClouDNS API provider for octoDNS
17
32
 
@@ -64,5 +79,3 @@ ClouDNSProvider does not support dynamic records.
64
79
  ### Development
65
80
 
66
81
  See the [/script/](/script/) directory for some tools to help with the development process. They generally follow the [Script to rule them all](https://github.com/github/scripts-to-rule-them-all) pattern. Most useful is `./script/bootstrap` which will create a venv and install both the runtime and development related requirements. It will also hook up a pre-commit hook that covers most of what's run by CI.
67
-
68
-
@@ -6,4 +6,5 @@ octodns_cloudns.egg-info/PKG-INFO
6
6
  octodns_cloudns.egg-info/SOURCES.txt
7
7
  octodns_cloudns.egg-info/dependency_links.txt
8
8
  octodns_cloudns.egg-info/requires.txt
9
- octodns_cloudns.egg-info/top_level.txt
9
+ octodns_cloudns.egg-info/top_level.txt
10
+ tests/test_octodns_cloudns_provider.py
@@ -0,0 +1,214 @@
1
+ import unittest
2
+ from unittest.mock import Mock, patch
3
+ from octodns.zone import Zone
4
+ from octodns.record import Record
5
+ from octodns.provider.cloudns import (
6
+ ClouDNSProvider,
7
+ ClouDNSClient,
8
+ ClouDNSClientUnknownDomainName
9
+ )
10
+
11
+ class TestClouDNSClient(unittest.TestCase):
12
+
13
+ def setUp(self):
14
+ self.client = ClouDNSClient('456', '123456')
15
+
16
+ @patch('requests.Session.get')
17
+ def test_raw_request_success(self, mock_get):
18
+ mock_response = Mock()
19
+ mock_response.status_code = 200
20
+ mock_response.text = 'Success response'
21
+ mock_get.return_value = mock_response
22
+
23
+ response = self.client._raw_request('dns/get-zone-info', 'domain-name=example.com')
24
+
25
+ self.assertEqual(response.text, 'Success response')
26
+ mock_get.assert_called_with('https://apidev.cloudns.net/dns/get-zone-info.json?auth-id=456&auth-password=123456&domain-name=example.com')
27
+
28
+ class TestClouDNSProvider(unittest.TestCase):
29
+ def setUp(self):
30
+ self.provider = ClouDNSProvider('test', '456', '123456')
31
+
32
+ # Invalid authentication
33
+ @patch('octodns.provider.cloudns.ClouDNSClient')
34
+ def test_invalid_authentication(self, mock_clouDNSClient):
35
+ # Arrange
36
+ mock_clouDNSClient.side_effect = Exception('Invalid authentication, incorrect username, ID, or password.')
37
+
38
+ # Act & Assert
39
+ with self.assertRaises(Exception) as context:
40
+ ClouDNSProvider('test', 'invalid_user_id', 'invalid_password')
41
+
42
+ self.assertTrue('Invalid authentication' in str(context.exception))
43
+
44
+ # Invalid Domain
45
+ @patch('octodns.provider.cloudns.ClouDNSClient.zone_records')
46
+ def test_populate_creates_zone_when_not_exist(self, mock_zone_records):
47
+ provider = ClouDNSProvider('test', '456', '123456')
48
+ zone = Zone('venkofgdge.invalidtld.', [])
49
+
50
+ mock_zone_records.side_effect = ClouDNSClientUnknownDomainName('Missing domain name')
51
+
52
+ with self.assertRaises(ClouDNSClientUnknownDomainName):
53
+ provider.populate(zone)
54
+
55
+ # Zone creation success
56
+ @patch('octodns.provider.cloudns.ClouDNSClient.zone_create')
57
+ def test_zone_create(self, mock_zone_create):
58
+ provider = ClouDNSProvider('test', '456', '123456')
59
+ zone = Zone('example552525.com.', [])
60
+
61
+ mock_zone_create.return_value = {'status': 'Success'}
62
+ response = provider._client.zone_create(zone.name[:-1], 'master')
63
+
64
+ mock_zone_create.assert_called_once_with('example552525.com', 'master')
65
+ self.assertEqual(response['status'], 'Success')
66
+
67
+ @patch.object(ClouDNSClient, '_request')
68
+ def test_zone_create_failure(self, mock_request):
69
+ mock_request.return_value = {'status': 'Failed'}
70
+ response = self.provider.zone_create('example.cosdm', 'master')
71
+
72
+ self.assertEqual(response['status'], 'Failed')
73
+
74
+ # Test records
75
+ @patch('octodns.provider.cloudns.ClouDNSProvider.populate')
76
+ def test_populate_creates_A_record(self, mock_populate):
77
+ provider = ClouDNSProvider('test', '456', '123456')
78
+ zone_name = 'example786.com.'
79
+ zone = Zone(zone_name, [])
80
+
81
+ mock_populate.return_value = {'status': 'Success'}
82
+
83
+ provider.populate(zone)
84
+
85
+ expected_argument = zone
86
+ actual_argument = mock_populate.call_args[0][0]
87
+ self.assertEqual(actual_argument, expected_argument)
88
+
89
+ def test_record_creation(self):
90
+ zone_name = 'example.com.'
91
+ zone = Zone(zone_name, [])
92
+
93
+ a_record_data = {
94
+ 'type': 'A',
95
+ 'ttl': 3600,
96
+ 'values': ['1.2.3.4']
97
+ }
98
+ a_record = Record.new(zone, 'www', a_record_data)
99
+ self.assertEqual(a_record.ttl, 3600)
100
+ self.assertEqual(a_record.name, 'www')
101
+ self.assertEqual(a_record.data['value'], '1.2.3.4')
102
+
103
+ cname_record_data = {
104
+ 'type': 'CNAME',
105
+ 'ttl': 3600,
106
+ 'value': 'example.com.'
107
+ }
108
+ cname_record = Record.new(zone, 'sub', cname_record_data)
109
+ self.assertEqual(cname_record.ttl, 3600)
110
+ self.assertEqual(cname_record.name, 'sub')
111
+ self.assertEqual(cname_record.data['value'], 'example.com.')
112
+
113
+ mx_record_data = {
114
+ 'type': 'MX',
115
+ 'ttl': 3600,
116
+ 'values': [{'preference': 10, 'exchange': 'mailforwadrd.cloudns.net.'}]
117
+ }
118
+ mx_record = Record.new(zone, '', mx_record_data)
119
+ self.assertEqual(mx_record.ttl, 3600)
120
+ self.assertEqual(mx_record.name, '')
121
+ self.assertEqual(str(mx_record.data['value']), "'10 mailforwadrd.cloudns.net.'")
122
+
123
+ txt_record_data = {
124
+ 'type': 'TXT',
125
+ 'ttl': 3600,
126
+ 'value': 'v=spf1 -a -mx -ip:192.168.0.1'
127
+ }
128
+ txt_record = Record.new(zone, '', txt_record_data)
129
+ self.assertEqual(txt_record.ttl, 3600)
130
+ self.assertEqual(txt_record.name, '')
131
+ self.assertEqual(txt_record.data['value'], "v=spf1 -a -mx -ip:192.168.0.1")
132
+
133
+ srv_record_data = {
134
+ 'type': 'SRV',
135
+ 'ttl': 3600,
136
+ 'values': [{'priority': 10, 'weight': 50, 'port': 443, 'target': 'bigbox.example.com.'}]
137
+ }
138
+ srv_record = Record.new(zone, '_sip._tcp', srv_record_data)
139
+ self.assertEqual(srv_record.ttl, 3600)
140
+ self.assertEqual(srv_record.name, '_sip._tcp')
141
+ self.assertEqual(str(srv_record.data['value']), "'10 50 443 bigbox.example.com.'")
142
+
143
+ loc_record_data = {
144
+ 'type': 'LOC',
145
+ 'ttl': 3600,
146
+ 'values': [{"lat_degrees": 10, "lat_minutes": 11 ,"lat_seconds": 12, "lat_direction": 'S',
147
+ "long_degrees": 13, "long_minutes": 14, "long_seconds": 15, "long_direction": 'W',
148
+ "altitude": 16, "size": 17, "precision_horz": 20, "precision_vert": 21}]
149
+ }
150
+ loc_record = Record.new(zone, '', loc_record_data)
151
+ self.assertEqual(loc_record.ttl, 3600)
152
+ self.assertEqual(loc_record.name, '')
153
+ self.assertEqual(str(loc_record.data['value']), "'10 11 12.000 S 13 14 15.000 W 16.00m 17.00m 20.00m 21.00m'")
154
+
155
+ sshfp_record_data = {
156
+ 'type': 'SSHFP',
157
+ 'ttl': 3600,
158
+ 'values': [{"algorithm": 1, "fingerprint_type": 1 ,"fingerprint": '123456789abcdef67890123456789abcdef67890'}]
159
+ }
160
+ sshfp_record = Record.new(zone, '', sshfp_record_data)
161
+ self.assertEqual(sshfp_record.ttl, 3600)
162
+ self.assertEqual(sshfp_record.name, '')
163
+ self.assertEqual(str(sshfp_record.data['value']), "'1 1 123456789abcdef67890123456789abcdef67890'")
164
+
165
+ naptr_record_data = {
166
+ 'type': 'NAPTR',
167
+ 'ttl': 3600,
168
+ 'values': [{"order": 10, "preference": 100, "flags": 'S', "service": 'SIP D2U',
169
+ "regexp": '$!sip:info@bar.example.com!', "replacement": ''}]
170
+ }
171
+ naptr_record = Record.new(zone, '', naptr_record_data)
172
+ self.assertEqual(naptr_record.ttl, 3600)
173
+ self.assertEqual(naptr_record.name, '')
174
+ self.assertEqual(str(naptr_record.data['value']), "'10 100 \"S\" \"SIP D2U\" \"$!sip:info@bar.example.com!\" '")
175
+
176
+ tlsa_record_data = {
177
+ 'type': 'TLSA',
178
+ 'ttl': 3600,
179
+ 'values': [{"certificate_association_data": 'F34834E4BEB8DCBE0D289E3B0F3BEAB16495620088B5CE9EF766E56254B80944', "certificate_usage": 0,
180
+ "selector": '1', "matching_type": 1}]
181
+ }
182
+ tlsa_record = Record.new(zone, '_443._tcp', tlsa_record_data)
183
+ self.assertEqual(tlsa_record.ttl, 3600)
184
+ self.assertEqual(tlsa_record.name, '_443._tcp')
185
+
186
+ geo_a_record_data = {
187
+ 'type': 'A',
188
+ 'ttl': 3600,
189
+ 'values': ['192.168.1.1'],
190
+ 'geo': {
191
+ 'AF': [
192
+ '2.2.3.4',
193
+ '2.2.3.5'
194
+ ],
195
+ 'AS-JP': [
196
+ '3.2.3.4',
197
+ '3.2.3.5'
198
+ ],
199
+ 'NA-US-CA': [
200
+ '4.2.3.4',
201
+ '4.2.3.5'
202
+ ]
203
+ }
204
+ }
205
+
206
+ geo_a_record = Record.new(zone, '', geo_a_record_data)
207
+ self.assertEqual(geo_a_record.ttl, 3600)
208
+ self.assertEqual(geo_a_record.name, '')
209
+ self.assertEqual(str(geo_a_record.data['value']), "192.168.1.1")
210
+ self.assertEqual(str(geo_a_record.data['geo']), "{'AF': ['2.2.3.4', '2.2.3.5'], 'AS-JP': ['3.2.3.4', '3.2.3.5'], 'NA-US-CA': ['4.2.3.4', '4.2.3.5']}")
211
+
212
+
213
+ if __name__ == '__main__':
214
+ unittest.main()
File without changes
@@ -2,15 +2,15 @@ octodns>=0.9.17
2
2
  requests>=2.27.0
3
3
 
4
4
  [dev]
5
+ pytest
6
+ pytest-cov
7
+ pytest-network
8
+ requests_mock
5
9
  black<24.0.0,>=23.1.0
6
10
  build>=0.7.0
7
11
  isort>=5.11.5
8
12
  pyflakes>=2.2.0
9
- pytest
10
- pytest-cov
11
- pytest-network
12
13
  readme_renderer[md]>=26.0
13
- requests_mock
14
14
  twine>=3.4.2
15
15
 
16
16
  [test]