예제 #1
0
    def test_not_exist_domain(self, fake_http):
        fake_http.get(f'{self.API_URL}/', status_code=404, json='')
        fake_http.head(f'{self.API_URL}/',
                       headers={'X-Total-Count': str(len(self.domain))})

        fake_http.post(f'{self.API_URL}/',
                       json={
                           "name": "unit.tests",
                           "create_date": 1507154178,
                           "id": 100000
                       })
        fake_http.get(f'{self.API_URL}/unit.tests/records/', json=list())
        fake_http.head(f'{self.API_URL}/unit.tests/records/',
                       headers={'X-Total-Count': str(len(self.api_record))})
        fake_http.post(f'{self.API_URL}/100000/records/', 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))
예제 #2
0
    def test_plan_with_unsupported_type(self):
        zone = Zone('unit.tests.', [])

        # supported
        supported = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4'
        })
        zone.add_record(supported)
        # not supported
        not_supported = Record.new(
            zone,
            'aaaa',
            {
                'ttl': 30,
                'type': 'AAAA',
                'value': '2601:644:500:e210:62f8:1dff:feb8:947a',
            },
        )
        zone.add_record(not_supported)
        provider = HelperProvider()
        plan = provider.plan(zone)
        self.assertTrue(plan)
        self.assertEqual(1, len(plan.changes))
        self.assertEqual(supported, plan.changes[0].new)
예제 #3
0
    def test_populate_in_addr_arpa(self):

        got = Zone('3.2.10.in-addr.arpa.', [])
        self.source.populate(got)

        expected = Zone('3.2.10.in-addr.arpa.', [])
        for name, data in (
            ('10', {
                'type': 'PTR',
                'ttl': 3600,
                'value': 'a-ptr.example.com.'
            }),
            ('11', {
                'type': 'PTR',
                'ttl': 30,
                'value': 'a-ptr-2.example.com.'
            }),
            ('8', {
                'type': 'PTR',
                'ttl': 3600,
                'value': 'has-dup-def123.example.com.'
            }),
            ('7', {
                'type': 'PTR',
                'ttl': 1800,
                'value': 'some-host-abc123.example.com.'
            }),
        ):
            record = Record.new(expected, name, data)
            expected.add_record(record)

        changes = expected.changes(got, SimpleProvider())
        self.assertEqual([], changes)
    def test_extra_change_no_health_check(self):
        provider, stubber = self._get_stubbed_provider()

        list_hosted_zones_resp = {
            'HostedZones': [{
                'Name': 'unit.tests.',
                'Id': 'z42',
                'CallerReference': 'abc',
            }],
            'Marker':
            'm',
            'IsTruncated':
            False,
            'MaxItems':
            '100',
        }
        stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {})

        # record with geo and no health check returns change
        desired = Zone('unit.tests.', [])
        record = Record.new(
            desired, 'a', {
                'ttl': 30,
                'type': 'A',
                'value': '1.2.3.4',
                'geo': {
                    'NA': ['2.2.3.4'],
                }
            })
        desired.add_record(record)

        extra = provider._extra_changes(desired, [])
        self.assertEquals(0, len(extra))
        stubber.assert_no_pending_responses()
    def test_safe_deletes_min_existing(self):
        # MAX_SAFE_DELETE_PCENT+1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Delete(record) for i in range(
                int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_DELETE_PCENT) +
                1)
        ]

        with self.assertRaises(UnsafePlan):
            Plan(zone, zone, changes).raise_if_unsafe()
예제 #6
0
    def test_safe_updates_min_existing(self):
        # MAX_SAFE_UPDATE_PCENT+1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Update(record, record) for i in range(
                int(Plan.MIN_EXISTING_RECORDS * Plan.MAX_SAFE_UPDATE_PCENT) +
                1)
        ]

        with self.assertRaises(UnsafePlan) as ctx:
            Plan(zone, zone, changes).raise_if_unsafe()

        self.assertTrue('Too many updates' in ctx.exception.message)
예제 #7
0
    def test_unsupporting(self):

        class NoAaaaProvider(object):
            id = 'no-aaaa'
            SUPPORTS_GEO = False

            def supports(self, record):
                return record._type != 'AAAA'

        current = Zone('unit.tests.', [])

        desired = Zone('unit.tests.', [])
        a = ARecord(desired, 'a', {'ttl': 42, 'value': '1.1.1.1'})
        desired.add_record(a)
        aaaa = AaaaRecord(desired, 'b', {'ttl': 42, 'value': '1:1:1::1'})
        desired.add_record(aaaa)

        # Only create the supported A, not the AAAA
        changes = current.changes(desired, NoAaaaProvider())
        self.assertEquals(1, len(changes))
        self.assertIsInstance(changes[0], Create)

        # Only delete the supported A, not the AAAA
        changes = desired.changes(current, NoAaaaProvider())
        self.assertEquals(1, len(changes))
        self.assertIsInstance(changes[0], Delete)
예제 #8
0
    def test_safe_deletes_min_existing_override(self):
        safe_pcent = .4
        # 40% + 1 fails when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(
                Record.new(zone, str(i), {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))

        changes = [
            Delete(record)
            for i in range(int(Plan.MIN_EXISTING_RECORDS * safe_pcent) + 1)
        ]

        with self.assertRaises(UnsafePlan) as ctx:
            Plan(zone, zone, changes,
                 delete_pcent_threshold=safe_pcent).raise_if_unsafe()

        self.assertTrue('Too many deletes' in ctx.exception.message)
예제 #9
0
    def test_not_included_records(self):
        zone_normal = Zone('unit.tests.', [])
        zone_included = Zone('unit.tests.', [])
        zone_missing = Zone('unit.tests.', [])

        normal = Record.new(zone_normal, 'www', {
            'ttl': 60,
            'type': 'A',
            'value': '9.9.9.9',
        })
        zone_normal.add_record(normal)

        included = Record.new(zone_included, 'www', {
            'octodns': {
                'included': ['not-here']
            },
            'ttl': 60,
            'type': 'A',
            'value': '9.9.9.9',
        })
        zone_included.add_record(included)

        provider = SimpleProvider()

        self.assertFalse(zone_normal.changes(zone_included, provider))
        self.assertTrue(zone_normal.changes(zone_missing, provider))

        self.assertFalse(zone_included.changes(zone_normal, provider))
        self.assertFalse(zone_included.changes(zone_missing, provider))

        self.assertTrue(zone_missing.changes(zone_normal, provider))
        self.assertFalse(zone_missing.changes(zone_included, provider))
예제 #10
0
    def test_apply_not_found(self):
        provider = EasyDNSProvider('test', 'token', 'apikey')

        wanted = Zone('unit.tests.', [])
        wanted.add_record(Record.new(wanted, 'test1', {
            "name": "test1",
            "ttl": 300,
            "type": "A",
            "value": "1.2.3.4",
        }))

        with requests_mock() as mock:
            base = 'https://rest.easydns.net/'
            mock.get('{}{}'.format(base, 'domain/unit.tests'), status_code=404,
                     text='{"id":"not_found","message":"The resource you '
                     'were accessing could not be found."}')
            mock.put('{}{}'.format(base, 'domains/add/unit.tests'),
                     status_code=200,
                     text='{"id":"OK","message":"Zone created."}')
            mock.get('{}{}'.format(base, 'zones/records/parsed/unit.tests'),
                     status_code=404,
                     text='{"id":"not_found","message":"The resource you '
                     'were accessing could not be found."}')
            mock.get('{}{}'.format(base, 'zones/records/all/unit.tests'),
                     status_code=404,
                     text='{"id":"not_found","message":"The resource you '
                     'were accessing could not be found."}')

            plan = provider.plan(wanted)
            self.assertFalse(plan.exists)
            self.assertEquals(1, len(plan.changes))
            with self.assertRaises(Exception) as ctx:
                provider.apply(plan)

            self.assertEquals('Not Found', text_type(ctx.exception))
예제 #11
0
    def test_base_provider(self):
        with self.assertRaises(NotImplementedError) as ctx:
            BaseProvider('base')
        self.assertEquals('Abstract base class, log property missing',
                          ctx.exception.message)

        class HasLog(BaseProvider):
            log = getLogger('HasLog')

        with self.assertRaises(NotImplementedError) as ctx:
            HasLog('haslog')
        self.assertEquals('Abstract base class, SUPPORTS_GEO property missing',
                          ctx.exception.message)

        class HasSupportsGeo(HasLog):
            SUPPORTS_GEO = False

        zone = Zone('unit.tests.', [])
        with self.assertRaises(NotImplementedError) as ctx:
            HasSupportsGeo('hassupportesgeo').populate(zone)
        self.assertEquals('Abstract base class, SUPPORTS property missing',
                          ctx.exception.message)

        class HasSupports(HasSupportsGeo):
            SUPPORTS = set(('A',))
        with self.assertRaises(NotImplementedError) as ctx:
            HasSupports('hassupportes').populate(zone)
        self.assertEquals('Abstract base class, populate method missing',
                          ctx.exception.message)

        class HasPopulate(HasSupports):

            def populate(self, zone, target=False, lenient=False):
                zone.add_record(Record.new(zone, '', {
                    'ttl': 60,
                    'type': 'A',
                    'value': '2.3.4.5'
                }))
                zone.add_record(Record.new(zone, 'going', {
                    'ttl': 60,
                    'type': 'A',
                    'value': '3.4.5.6'
                }))

        zone.add_record(Record.new(zone, '', {
            'ttl': 60,
            'type': 'A',
            'value': '1.2.3.4'
        }))

        self.assertTrue(HasSupports('hassupportesgeo')
                        .supports(list(zone.records)[0]))

        plan = HasPopulate('haspopulate').plan(zone)
        self.assertEquals(2, len(plan.changes))

        with self.assertRaises(NotImplementedError) as ctx:
            HasPopulate('haspopulate').apply(plan)
        self.assertEquals('Abstract base class, _apply method missing',
                          ctx.exception.message)
    def test_plan_disappearing_ns_records(self):
        expected = Zone('unit.tests.', [])
        expected.add_record(
            Record.new(expected, '', {
                'type': 'NS',
                'ttl': 600,
                'values': ['8.8.8.8.', '9.9.9.9.']
            }))
        expected.add_record(
            Record.new(expected, 'sub', {
                'type': 'NS',
                'ttl': 600,
                'values': ['8.8.8.8.', '9.9.9.9.']
            }))
        with requests_mock() as mock:
            mock.get(re.compile('domains$'),
                     status_code=200,
                     text=LIST_DOMAINS_RESPONSE)
            mock.get(re.compile('records'), status_code=200, text=EMPTY_TEXT)

            plan = self.provider.plan(expected)
            self.assertTrue(mock.called)
            self.assertTrue(plan.exists)

            # OctoDNS does not propagate top-level NS records.
            self.assertEquals(1, len(plan.changes))
    def _test_apply_with_data(self, data):
        expected = Zone('unit.tests.', [])
        for record in data.OtherRecords:
            expected.add_record(
                Record.new(expected, record['subdomain'], record['data']))

        with requests_mock() as list_mock:
            list_mock.get(re.compile('domains$'),
                          status_code=200,
                          text=LIST_DOMAINS_RESPONSE)
            list_mock.get(re.compile('records'),
                          status_code=200,
                          json=data.OwnRecords)
            plan = self.provider.plan(expected)
            self.assertTrue(list_mock.called)
            if not data.ExpectChanges:
                self.assertFalse(plan)
                return

        with requests_mock() as mock:
            called = set()

            def make_assert_sending_right_body(expected):
                def _assert_sending_right_body(request, _context):
                    called.add(request.method)
                    if request.method != 'DELETE':
                        self.assertEqual(request.headers['content-type'],
                                         'application/json')
                        self.assertDictEqual(expected,
                                             json.loads(request.body))
                    else:
                        parts = urlparse(request.url)
                        self.assertEqual(expected, parts.query)
                    return ''

                return _assert_sending_right_body

            mock.get(re.compile('domains$'),
                     status_code=200,
                     text=LIST_DOMAINS_RESPONSE)
            mock.post(re.compile('domains/.*/records$'),
                      status_code=202,
                      text=make_assert_sending_right_body(
                          data.ExpectedAdditions))
            mock.delete(re.compile('domains/.*/records?.*'),
                        status_code=202,
                        text=make_assert_sending_right_body(
                            data.ExpectedDeletions))
            mock.put(re.compile('domains/.*/records$'),
                     status_code=202,
                     text=make_assert_sending_right_body(data.ExpectedUpdates))

            self.provider.apply(plan)
            self.assertTrue(data.ExpectedAdditions is None or "POST" in called)
            self.assertTrue(data.ExpectedDeletions is None
                            or "DELETE" in called)
            self.assertTrue(data.ExpectedUpdates is None or "PUT" in called)
예제 #14
0
    def test_process_desired_zone(self):
        provider = HelperProvider('test')

        # SUPPORTS_MULTIVALUE_PTR
        provider.SUPPORTS_MULTIVALUE_PTR = False
        zone1 = Zone('unit.tests.', [])
        record1 = Record.new(zone1, 'ptr', {
            'type': 'PTR',
            'ttl': 3600,
            'values': ['foo.com.', 'bar.com.'],
        })
        zone1.add_record(record1)

        zone2 = provider._process_desired_zone(zone1.copy())
        record2 = list(zone2.records)[0]
        self.assertEqual(len(record2.values), 1)

        provider.SUPPORTS_MULTIVALUE_PTR = True
        zone2 = provider._process_desired_zone(zone1.copy())
        record2 = list(zone2.records)[0]
        from pprint import pprint
        pprint([record1, record2])
        self.assertEqual(len(record2.values), 2)

        # SUPPORTS_DYNAMIC
        provider.SUPPORTS_DYNAMIC = False
        zone1 = Zone('unit.tests.', [])
        record1 = Record.new(
            zone1, 'a', {
                'dynamic': {
                    'pools': {
                        'one': {
                            'values': [{
                                'value': '1.1.1.1',
                            }],
                        },
                    },
                    'rules': [{
                        'pool': 'one',
                    }],
                },
                'type': 'A',
                'ttl': 3600,
                'values': ['2.2.2.2'],
            })
        self.assertTrue(record1.dynamic)
        zone1.add_record(record1)

        zone2 = provider._process_desired_zone(zone1.copy())
        record2 = list(zone2.records)[0]
        self.assertFalse(record2.dynamic)

        provider.SUPPORTS_DYNAMIC = True
        zone2 = provider._process_desired_zone(zone1.copy())
        record2 = list(zone2.records)[0]
        self.assertTrue(record2.dynamic)
    def test_extra_change_no_health_check(self):
        provider, stubber = self._get_stubbed_provider()

        list_hosted_zones_resp = {
            'HostedZones': [{
                'Name': 'unit.tests.',
                'Id': 'z42',
                'CallerReference': 'abc',
            }],
            'Marker':
            'm',
            'IsTruncated':
            False,
            'MaxItems':
            '100',
        }
        stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {})

        # record with geo and no health check returns change
        existing = Zone('unit.tests.', [])
        record = Record.new(
            existing, 'a', {
                'ttl': 30,
                'type': 'A',
                'value': '1.2.3.4',
                'geo': {
                    'NA': ['2.2.3.4'],
                }
            })
        existing.add_record(record)
        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'})
        extra = provider._extra_changes(existing, [])
        self.assertEquals(1, len(extra))
        stubber.assert_no_pending_responses()
예제 #16
0
    def test_include_change(self):
        zone = Zone('unit.tests.', [])

        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })
        zone.add_record(record)
        provider = HelperProvider([], include_change_callback=lambda c: False)
        plan = provider.plan(zone)
        # We filtered out the only change
        self.assertFalse(plan)
예제 #17
0
    def test_process_desired_zone(self):
        zone1 = Zone('unit.tests.', [])
        record1 = Record.new(zone1, 'ptr', {
            'type': 'PTR',
            'ttl': 3600,
            'values': ['foo.com.', 'bar.com.'],
        })
        zone1.add_record(record1)

        zone2 = HelperProvider('hasptr')._process_desired_zone(zone1)
        record2 = list(zone2.records)[0]

        self.assertEqual(len(record2.values), 1)
예제 #18
0
    def test_copy(self):
        zone = Zone('unit.tests.', [])

        a = ARecord(zone, 'a', {'ttl': 42, 'value': '1.1.1.1'})
        zone.add_record(a)
        b = ARecord(zone, 'b', {'ttl': 42, 'value': '1.1.1.2'})
        zone.add_record(b)

        # Sanity check
        self.assertEqualsNameAndValues(set((a, b)), zone.records)

        copy = zone.copy()
        # We have an origin set and it is the source/original zone
        self.assertEquals(zone, copy._origin)
        # Our records are zone's records to start (references)
        self.assertEqualsNameAndValues(zone.records, copy.records)

        # If we try and change something that's already there we realize and
        # then get an error about a duplicate
        b_prime = ARecord(zone, 'b', {'ttl': 42, 'value': '1.1.1.3'})
        with self.assertRaises(DuplicateRecordException):
            copy.add_record(b_prime)
        self.assertIsNone(copy._origin)
        # Unchanged, straight copies
        self.assertEqualsNameAndValues(zone.records, copy.records)

        # If we add with replace things will be realized and the record will
        # have changed
        copy = zone.copy()
        copy.add_record(b_prime, replace=True)
        self.assertIsNone(copy._origin)
        self.assertEqualsNameAndValues(set((a, b_prime)), copy.records)

        # If we add another record, things are reliazed and it has been added
        copy = zone.copy()
        c = ARecord(zone, 'c', {'ttl': 42, 'value': '1.1.1.3'})
        copy.add_record(c)
        self.assertEqualsNameAndValues(set((a, b, c)), copy.records)

        # If we remove a record, things are reliazed and it has been removed
        copy = zone.copy()
        copy.remove_record(a)
        self.assertEqualsNameAndValues(set((b, )), copy.records)

        # Re-realizing is a noop
        copy = zone.copy()
        # Happens the first time
        self.assertTrue(copy.hydrate())
        # Doesn't the second
        self.assertFalse(copy.hydrate())
    def test_populate_normal_sub1(self):
        got = Zone('asdf.subtest.com.', [])
        self.source.populate(got)
        self.assertEquals(1, len(got.records))

        expected = Zone('asdf.subtest.com.', [])
        for name, data in (('a3', {
                'type': 'A',
                'ttl': 3600,
                'values': ['10.2.3.7'],
        }), ):
            record = Record.new(expected, name, data)
            expected.add_record(record)

        changes = expected.changes(got, SimpleProvider())
        self.assertEquals([], changes)
    def test_cdn_alias(self):
        provider = CloudflareProvider('test', 'email', 'token', True)

        # A CNAME for us to transform to ALIAS
        provider.zone_records = Mock(return_value=[
            {
                "id": "fc12ab34cd5611334422ab3322997642",
                "type": "CNAME",
                "name": "unit.tests",
                "content": "www.unit.tests",
                "proxiable": True,
                "proxied": True,
                "ttl": 300,
                "locked": False,
                "zone_id": "ff12ab34cd5611334422ab3322997650",
                "zone_name": "unit.tests",
                "modified_on": "2017-03-11T18:01:43.420689Z",
                "created_on": "2017-03-11T18:01:43.420689Z",
                "meta": {
                    "auto_added": False
                }
            },
        ])

        zone = Zone('unit.tests.', [])
        provider.populate(zone)
        self.assertEquals(1, len(zone.records))
        record = list(zone.records)[0]
        self.assertEquals('', record.name)
        self.assertEquals('unit.tests.', record.fqdn)
        self.assertEquals('ALIAS', record._type)
        self.assertEquals('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 itsself.
        wanted = Zone('unit.tests.', [])
        wanted.add_record(
            Record.new(
                wanted, '', {
                    'ttl': 300,
                    'type': 'ALIAS',
                    'value': 'change.unit.tests.cdn.cloudflare.net.'
                }))

        plan = provider.plan(wanted)
        self.assertEquals(False, hasattr(plan, 'changes'))
예제 #21
0
    def test_small_change(self):
        provider = PowerDnsProvider('test', 'non.existent', 'api-key')

        expected = Zone('unit.tests.', [])
        source = YamlProvider('test', join(dirname(__file__), 'config'))
        source.populate(expected)
        self.assertEquals(23, len(expected.records))

        # A small change to a single record
        with requests_mock() as mock:
            mock.get(ANY, status_code=200, text=FULL_TEXT)
            mock.get('http://non.existent:8081/api/v1/servers/localhost',
                     status_code=200,
                     json={'version': '4.1.0'})

            missing = Zone(expected.name, [])
            # Find and delete the SPF record
            for record in expected.records:
                if record._type != 'SPF':
                    missing.add_record(record)

            def assert_delete_callback(request, context):
                self.assertEquals(
                    {
                        'rrsets': [{
                            'records': [{
                                'content': '"v=spf1 ip4:192.168.0.1/16-all"',
                                'disabled': False
                            }],
                            'changetype':
                            'DELETE',
                            'type':
                            'SPF',
                            'name':
                            'spf.unit.tests.',
                            'ttl':
                            600
                        }]
                    }, loads(request.body))
                return ''

            mock.patch(ANY, status_code=201, text=assert_delete_callback)

            plan = provider.plan(missing)
            self.assertEquals(1, len(plan.changes))
            self.assertEquals(1, provider.apply(plan))
예제 #22
0
    def test_plan_with_root_ns(self):
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, '', {
            'ttl': 30,
            'type': 'NS',
            'value': '1.2.3.4.'
        })
        zone.add_record(record)

        # No root NS support, no change, thus no plan
        provider = HelperProvider()
        self.assertEqual(None, provider.plan(zone))

        # set Support root NS records, see the record
        provider.SUPPORTS_ROOT_NS = True
        plan = provider.plan(zone)
        self.assertTrue(plan)
        self.assertEqual(1, len(plan.changes))
예제 #23
0
    def test_safe_min_existing_creates(self):
        # Creates are safe when existing records is over MIN_EXISTING_RECORDS
        zone = Zone('unit.tests.', [])

        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(Record.new(zone, str(i), {
                            'ttl': 60,
                            'type': 'A',
                            'value': '2.3.4.5'
                            }))

        Plan(zone, zone, [Create(record) for i in range(10)]).raise_if_unsafe()
    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))
예제 #25
0
    def test_apply(self, fake_http):

        fake_http.get(f'{self.API_URL}/unit.tests/records/', json=list())
        fake_http.get(f'{self.API_URL}/', json=self.domain)
        fake_http.head(f'{self.API_URL}/unit.tests/records/',
                       headers={'X-Total-Count': '0'})
        fake_http.head(f'{self.API_URL}/',
                       headers={'X-Total-Count': str(len(self.domain))})
        fake_http.post(f'{self.API_URL}/100000/records/', 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))
    def test_no_extra_changes(self):
        provider, stubber = self._get_stubbed_provider()

        list_hosted_zones_resp = {
            'HostedZones': [{
                'Name': 'unit.tests.',
                'Id': 'z42',
                'CallerReference': 'abc',
            }],
            'Marker':
            'm',
            'IsTruncated':
            False,
            'MaxItems':
            '100',
        }
        stubber.add_response('list_hosted_zones', list_hosted_zones_resp, {})

        # empty is empty
        existing = Zone('unit.tests.', [])
        extra = provider._extra_changes(existing, [])
        self.assertEquals([], extra)
        stubber.assert_no_pending_responses()

        # single record w/o geo is empty
        existing = Zone('unit.tests.', [])
        record = Record.new(existing, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })
        existing.add_record(record)
        extra = provider._extra_changes(existing, [])
        self.assertEquals([], extra)
        stubber.assert_no_pending_responses()

        # short-circuit for unknown zone
        other = Zone('other.tests.', [])
        extra = provider._extra_changes(other, [])
        self.assertEquals([], extra)
        stubber.assert_no_pending_responses()
예제 #27
0
def octodns_test_zone():
    '''Load the unit.tests zone config into an octodns.zone.Zone object.'''

    zone = Zone('unit.tests.', [])
    source = YamlProvider('test', join(dirname(__file__), 'config'))
    source.populate(zone)
    # Replace the unit test fixture's NS record with one of ours.
    remove_octodns_record(zone, '', 'NS')
    zone.add_record(
        Record.new(
            zone, '', {
                'ttl':
                3600,
                'type':
                'NS',
                'values': [
                    'dns1.p01.nsone.net.', 'dns2.p01.nsone.net.',
                    'dns3.p01.nsone.net.', 'dns4.p01.nsone.net.'
                ]
            }))
    return zone
예제 #28
0
    def test_safe_updates_min_existing_pcent(self):
        # MAX_SAFE_UPDATE_PCENT is safe when more
        # than MIN_EXISTING_RECORDS exist
        zone = Zone('unit.tests.', [])
        record = Record.new(zone, 'a', {
            'ttl': 30,
            'type': 'A',
            'value': '1.2.3.4',
        })

        for i in range(int(Plan.MIN_EXISTING_RECORDS)):
            zone.add_record(Record.new(zone, str(i), {
                            'ttl': 60,
                            'type': 'A',
                            'value': '2.3.4.5'
                            }))
        changes = [Update(record, record)
                   for i in range(int(Plan.MIN_EXISTING_RECORDS *
                                      Plan.MAX_SAFE_UPDATE_PCENT))]

        Plan(zone, zone, changes).raise_if_unsafe()
    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": "*****@*****.**"
        })

        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)
예제 #30
0
    def test_cname_loop(self):
        source = EtcHostsProvider('test', path.join(dirname(__file__),
                                                    'config'))

        zone = Zone('unit.tests.', [])

        # We never populate anything, when acting as a source
        source.populate(zone, target=source)
        self.assertEquals(0, len(zone.records))
        # Same if we're acting as a target
        source.populate(zone)
        self.assertEquals(0, len(zone.records))

        record = Record.new(zone, 'start', {
            'ttl': 60,
            'type': 'CNAME',
            'value': 'middle.unit.tests.',
        })
        zone.add_record(record)
        record = Record.new(zone, 'middle', {
            'ttl': 60,
            'type': 'CNAME',
            'value': 'loop.unit.tests.',
        })
        zone.add_record(record)
        record = Record.new(zone, 'loop', {
            'ttl': 60,
            'type': 'CNAME',
            'value': 'start.unit.tests.',
        })
        zone.add_record(record)

        with TemporaryDirectory() as td:
            # Add some subdirs to make sure that it can create them
            directory = path.join(td.dirname, 'sub', 'dir')
            hosts_file = path.join(directory, 'unit.tests.hosts')
            target = EtcHostsProvider('test', directory)

            # We add everything
            plan = target.plan(zone)
            self.assertEquals(len(zone.records), len(plan.changes))
            self.assertFalse(isfile(hosts_file))

            # Now actually do it
            self.assertEquals(len(zone.records), target.apply(plan))
            self.assertTrue(isfile(hosts_file))

            with open(hosts_file) as fh:
                data = fh.read()
                print(data)
                self.assertTrue('# loop.unit.tests -> start.unit.tests '
                                '**loop**' in data)
                self.assertTrue('# middle.unit.tests -> loop.unit.tests '
                                '**loop**' in data)
                self.assertTrue('# start.unit.tests -> middle.unit.tests '
                                '**loop**' in data)