mirror of
				https://github.com/github/octodns.git
				synced 2024-05-11 05:55:00 +00:00 
			
		
		
		
	Merge branch 'main' into rc-version-fix
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/main.yml
									
									
									
									
										vendored
									
									
								
							@@ -8,7 +8,7 @@ jobs:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
        # Tested versions based on dates in https://devguide.python.org/devcycle/#end-of-life-branches, 
 | 
			
		||||
        python-version: ['3.7', '3.8', '3.9', '3.10']
 | 
			
		||||
        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@master
 | 
			
		||||
      - name: Setup python
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,7 @@
 | 
			
		||||
## v1.0.0.rc2 - 2023-??-?? -
 | 
			
		||||
 | 
			
		||||
* Record and Zone validation now ensures there's no whitespace in names
 | 
			
		||||
 | 
			
		||||
## v1.0.0.rc0 - 2023-05-16 - First of the ones
 | 
			
		||||
 | 
			
		||||
#### Noteworthy changes
 | 
			
		||||
 
 | 
			
		||||
@@ -475,7 +475,6 @@ class Manager(object):
 | 
			
		||||
        force=False,
 | 
			
		||||
        plan_output_fh=stdout,
 | 
			
		||||
    ):
 | 
			
		||||
 | 
			
		||||
        self.log.info(
 | 
			
		||||
            'sync: eligible_zones=%s, eligible_targets=%s, dry_run=%s, '
 | 
			
		||||
            'force=%s, plan_output_fh=%s',
 | 
			
		||||
 
 | 
			
		||||
@@ -61,8 +61,13 @@ class AutoArpa(BaseProcessor):
 | 
			
		||||
                    zone,
 | 
			
		||||
                    name,
 | 
			
		||||
                    {'ttl': self.ttl, 'type': 'PTR', 'values': fqdns},
 | 
			
		||||
                    lenient=lenient,
 | 
			
		||||
                )
 | 
			
		||||
                zone.add_record(
 | 
			
		||||
                    record,
 | 
			
		||||
                    replace=self.populate_should_replace,
 | 
			
		||||
                    lenient=lenient,
 | 
			
		||||
                )
 | 
			
		||||
                zone.add_record(record, replace=self.populate_should_replace)
 | 
			
		||||
 | 
			
		||||
        self.log.info(
 | 
			
		||||
            'populate:   found %s records', len(zone.records) - before
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,6 @@ class Plan(object):
 | 
			
		||||
            self.existing
 | 
			
		||||
            and len(self.existing.records) >= self.MIN_EXISTING_RECORDS
 | 
			
		||||
        ):
 | 
			
		||||
 | 
			
		||||
            existing_record_count = len(self.existing.records)
 | 
			
		||||
            if existing_record_count > 0:
 | 
			
		||||
                update_pcent = (
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,10 @@ class Record(EqualityTupleMixin):
 | 
			
		||||
            # convert the error into a reason
 | 
			
		||||
            reasons.append(str(e))
 | 
			
		||||
            name = str(name)
 | 
			
		||||
 | 
			
		||||
        if ' ' in name or '\t' in name:
 | 
			
		||||
            reasons.append('invalid record, whitespace is not allowed')
 | 
			
		||||
 | 
			
		||||
        fqdn = f'{name}.{zone.name}' if name else zone.name
 | 
			
		||||
        try:
 | 
			
		||||
            _type = data['type']
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ class ValidationError(RecordException):
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def build_message(cls, fqdn, reasons):
 | 
			
		||||
        reasons = '\n  - '.join(reasons)
 | 
			
		||||
        return f'Invalid record {idna_decode(fqdn)}\n  - {reasons}'
 | 
			
		||||
        return f'Invalid record "{idna_decode(fqdn)}"\n  - {reasons}'
 | 
			
		||||
 | 
			
		||||
    def __init__(self, fqdn, reasons):
 | 
			
		||||
        super().__init__(self.build_message(fqdn, reasons))
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseSource(object):
 | 
			
		||||
 | 
			
		||||
    SUPPORTS_MULTIVALUE_PTR = False
 | 
			
		||||
    SUPPORTS_POOL_VALUE_STATUS = False
 | 
			
		||||
    SUPPORTS_ROOT_NS = False
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,9 @@ class Zone(object):
 | 
			
		||||
    def __init__(self, name, sub_zones):
 | 
			
		||||
        if not name[-1] == '.':
 | 
			
		||||
            raise Exception(f'Invalid zone name {name}, missing ending dot')
 | 
			
		||||
        elif ' ' in name or '\t' in name:
 | 
			
		||||
            raise Exception(f'Invalid zone name {name}, whitespace not allowed')
 | 
			
		||||
 | 
			
		||||
        # internally everything is idna
 | 
			
		||||
        self.name = idna_encode(str(name)) if name else name
 | 
			
		||||
        # we'll keep a decoded version around for logs and errors
 | 
			
		||||
 
 | 
			
		||||
@@ -1,50 +1,46 @@
 | 
			
		||||
# DO NOT EDIT THIS FILE DIRECTLY - use ./script/update-requirements to update
 | 
			
		||||
Pygments==2.13.0
 | 
			
		||||
attrs==22.1.0
 | 
			
		||||
black==22.10.0
 | 
			
		||||
bleach==5.0.1
 | 
			
		||||
build==0.9.0
 | 
			
		||||
certifi==2022.12.7
 | 
			
		||||
Pygments==2.15.1
 | 
			
		||||
black==23.3.0
 | 
			
		||||
bleach==6.0.0
 | 
			
		||||
build==0.10.0
 | 
			
		||||
certifi==2023.5.7
 | 
			
		||||
cffi==1.15.1
 | 
			
		||||
charset-normalizer==2.1.1
 | 
			
		||||
charset-normalizer==3.1.0
 | 
			
		||||
click==8.1.3
 | 
			
		||||
cmarkgfm==2022.10.27
 | 
			
		||||
commonmark==0.9.1
 | 
			
		||||
coverage==6.5.0
 | 
			
		||||
docutils==0.19
 | 
			
		||||
exceptiongroup==1.0.0
 | 
			
		||||
importlib-metadata==5.0.0
 | 
			
		||||
iniconfig==1.1.1
 | 
			
		||||
coverage==7.2.5
 | 
			
		||||
docutils==0.20.1
 | 
			
		||||
importlib-metadata==6.6.0
 | 
			
		||||
iniconfig==2.0.0
 | 
			
		||||
isort==5.11.4
 | 
			
		||||
jaraco.classes==3.2.3
 | 
			
		||||
keyring==23.9.3
 | 
			
		||||
more-itertools==9.0.0
 | 
			
		||||
mypy-extensions==0.4.3
 | 
			
		||||
packaging==21.3
 | 
			
		||||
pathspec==0.10.1
 | 
			
		||||
pep517==0.13.0
 | 
			
		||||
pkginfo==1.8.3
 | 
			
		||||
platformdirs==2.5.2
 | 
			
		||||
keyring==23.13.1
 | 
			
		||||
markdown-it-py==2.2.0
 | 
			
		||||
mdurl==0.1.2
 | 
			
		||||
more-itertools==9.1.0
 | 
			
		||||
mypy-extensions==1.0.0
 | 
			
		||||
packaging==23.1
 | 
			
		||||
pathspec==0.11.1
 | 
			
		||||
pkginfo==1.9.6
 | 
			
		||||
platformdirs==3.5.1
 | 
			
		||||
pluggy==1.0.0
 | 
			
		||||
pprintpp==0.4.0
 | 
			
		||||
pycountry-convert==0.7.2
 | 
			
		||||
pycountry==22.3.5
 | 
			
		||||
pycparser==2.21
 | 
			
		||||
pyflakes==2.5.0
 | 
			
		||||
pyparsing==3.0.9
 | 
			
		||||
pyflakes==3.0.1
 | 
			
		||||
pyproject_hooks==1.0.0
 | 
			
		||||
pytest-cov==4.0.0
 | 
			
		||||
pytest-mock==3.10.0
 | 
			
		||||
pytest-network==0.0.1
 | 
			
		||||
pytest==7.2.0
 | 
			
		||||
pytest==7.3.1
 | 
			
		||||
readme-renderer==37.3
 | 
			
		||||
repoze.lru==0.7
 | 
			
		||||
requests-toolbelt==0.10.1
 | 
			
		||||
requests==2.28.1
 | 
			
		||||
requests-toolbelt==1.0.0
 | 
			
		||||
requests==2.31.0
 | 
			
		||||
rfc3986==2.0.0
 | 
			
		||||
rich==12.6.0
 | 
			
		||||
tomli==2.0.1
 | 
			
		||||
twine==4.0.1
 | 
			
		||||
typing_extensions==4.4.0
 | 
			
		||||
urllib3==1.26.12
 | 
			
		||||
rich==13.3.5
 | 
			
		||||
twine==4.0.2
 | 
			
		||||
urllib3==2.0.2
 | 
			
		||||
webencodings==0.5.1
 | 
			
		||||
zipp==3.10.0
 | 
			
		||||
zipp==3.15.0
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,6 @@ PyYAML==6.0
 | 
			
		||||
dnspython==2.3.0
 | 
			
		||||
fqdn==1.5.1
 | 
			
		||||
idna==3.4
 | 
			
		||||
natsort==8.2.0
 | 
			
		||||
natsort==8.3.1
 | 
			
		||||
python-dateutil==2.8.2
 | 
			
		||||
six==1.16.0
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,12 @@ from unittest import TestCase
 | 
			
		||||
 | 
			
		||||
from octodns.processor.arpa import AutoArpa
 | 
			
		||||
from octodns.record import Record
 | 
			
		||||
from octodns.record.exception import ValidationError
 | 
			
		||||
from octodns.zone import Zone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestAutoArpa(TestCase):
 | 
			
		||||
    def test_empty_zone(self):
 | 
			
		||||
 | 
			
		||||
        # empty zone no records
 | 
			
		||||
        zone = Zone('unit.tests.', [])
 | 
			
		||||
        aa = AutoArpa('auto-arpa')
 | 
			
		||||
@@ -227,3 +227,39 @@ class TestAutoArpa(TestCase):
 | 
			
		||||
        arpa = Zone('0.10.in-addr.arpa.', [])
 | 
			
		||||
        aa.populate(arpa)
 | 
			
		||||
        self.assertEqual(0, len(arpa.records))
 | 
			
		||||
 | 
			
		||||
    def test_single_value_A_with_space(self):
 | 
			
		||||
        zone = Zone('unit.tests.', [])
 | 
			
		||||
 | 
			
		||||
        # invalid record without lenient
 | 
			
		||||
        with self.assertRaises(ValidationError):
 | 
			
		||||
            Record.new(
 | 
			
		||||
                zone,
 | 
			
		||||
                'a with spaces',
 | 
			
		||||
                {'ttl': 32, 'type': 'A', 'value': '1.2.3.4'},
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # invalid record with lenient
 | 
			
		||||
        lenient = True
 | 
			
		||||
        record = Record.new(
 | 
			
		||||
            zone,
 | 
			
		||||
            'a with spaces',
 | 
			
		||||
            {'ttl': 32, 'type': 'A', 'value': '1.2.3.4'},
 | 
			
		||||
            lenient=lenient,
 | 
			
		||||
        )
 | 
			
		||||
        zone.add_record(record)
 | 
			
		||||
        aa = AutoArpa('auto-arpa')
 | 
			
		||||
        aa.process_source_zone(zone, [])
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            {'4.3.2.1.in-addr.arpa.': {'a with spaces.unit.tests.'}},
 | 
			
		||||
            aa._records,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # matching zone
 | 
			
		||||
        arpa = Zone('3.2.1.in-addr.arpa.', [])
 | 
			
		||||
        aa.populate(arpa, lenient=lenient)
 | 
			
		||||
        self.assertEqual(1, len(arpa.records))
 | 
			
		||||
        (ptr,) = arpa.records
 | 
			
		||||
        self.assertEqual('4.3.2.1.in-addr.arpa.', ptr.fqdn)
 | 
			
		||||
        self.assertEqual(record.fqdn, ptr.value)
 | 
			
		||||
        self.assertEqual(3600, ptr.ttl)
 | 
			
		||||
 
 | 
			
		||||
@@ -398,6 +398,28 @@ class TestRecordValidation(TestCase):
 | 
			
		||||
    zone = Zone('unit.tests.', [])
 | 
			
		||||
 | 
			
		||||
    def test_base(self):
 | 
			
		||||
        # no spaces
 | 
			
		||||
        for name in (
 | 
			
		||||
            ' ',
 | 
			
		||||
            ' leading',
 | 
			
		||||
            'trailing ',
 | 
			
		||||
            'in the middle',
 | 
			
		||||
            '\t',
 | 
			
		||||
            '\tleading',
 | 
			
		||||
            'trailing\t',
 | 
			
		||||
            'in\tthe\tmiddle',
 | 
			
		||||
        ):
 | 
			
		||||
            with self.assertRaises(ValidationError) as ctx:
 | 
			
		||||
                Record.new(
 | 
			
		||||
                    self.zone,
 | 
			
		||||
                    name,
 | 
			
		||||
                    {'ttl': 300, 'type': 'A', 'value': '1.2.3.4'},
 | 
			
		||||
                )
 | 
			
		||||
            reason = ctx.exception.reasons[0]
 | 
			
		||||
            self.assertEqual(
 | 
			
		||||
                'invalid record, whitespace is not allowed', reason
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        # name = '@'
 | 
			
		||||
        with self.assertRaises(ValidationError) as ctx:
 | 
			
		||||
            name = '@'
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,6 @@ from octodns.zone import Zone
 | 
			
		||||
 | 
			
		||||
class TestRecordIp(TestCase):
 | 
			
		||||
    def test_ipv4_value_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # anything goes, we're a noop
 | 
			
		||||
        for s in (
 | 
			
		||||
            None,
 | 
			
		||||
 
 | 
			
		||||
@@ -68,7 +68,6 @@ class TestRecordMx(TestCase):
 | 
			
		||||
        a.__repr__()
 | 
			
		||||
 | 
			
		||||
    def test_mx_value_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # empty string won't parse
 | 
			
		||||
        with self.assertRaises(RrParseError):
 | 
			
		||||
            MxValue.parse_rdata_text('')
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,6 @@ class TestRecordPtr(TestCase):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def test_ptr_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # anything goes, we're a noop
 | 
			
		||||
        for s in (
 | 
			
		||||
            None,
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,6 @@ class TestRecordSrv(TestCase):
 | 
			
		||||
        a.__repr__()
 | 
			
		||||
 | 
			
		||||
    def test_srv_value_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # empty string won't parse
 | 
			
		||||
        with self.assertRaises(RrParseError):
 | 
			
		||||
            SrvValue.parse_rdata_text('')
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,6 @@ class TestRecordSshfp(TestCase):
 | 
			
		||||
        a.__repr__()
 | 
			
		||||
 | 
			
		||||
    def test_sshfp_value_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # empty string won't parse
 | 
			
		||||
        with self.assertRaises(RrParseError):
 | 
			
		||||
            SshfpValue.parse_rdata_text('')
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@ from octodns.zone import Zone
 | 
			
		||||
 | 
			
		||||
class TestRecordTarget(TestCase):
 | 
			
		||||
    def test_target_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # anything goes, we're a noop
 | 
			
		||||
        for s in (
 | 
			
		||||
            None,
 | 
			
		||||
 
 | 
			
		||||
@@ -118,7 +118,6 @@ class TestRecordTlsa(TestCase):
 | 
			
		||||
        a.__repr__()
 | 
			
		||||
 | 
			
		||||
    def test_tsla_value_rdata_text(self):
 | 
			
		||||
 | 
			
		||||
        # empty string won't parse
 | 
			
		||||
        with self.assertRaises(RrParseError):
 | 
			
		||||
            TlsaValue.parse_rdata_text('')
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,6 @@ class TestTinyDnsFileSource(TestCase):
 | 
			
		||||
        self.assertEqual([], changes)
 | 
			
		||||
 | 
			
		||||
    def test_populate_in_addr_arpa(self):
 | 
			
		||||
 | 
			
		||||
        got = Zone('3.2.10.in-addr.arpa.', [])
 | 
			
		||||
        self.source.populate(got)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -186,8 +186,12 @@ class TestZone(TestCase):
 | 
			
		||||
            Zone('not.allowed', [])
 | 
			
		||||
        self.assertTrue('missing ending dot' in str(ctx.exception))
 | 
			
		||||
 | 
			
		||||
    def test_sub_zones(self):
 | 
			
		||||
    def test_whitespace(self):
 | 
			
		||||
        with self.assertRaises(Exception) as ctx:
 | 
			
		||||
            Zone('space not allowed.', [])
 | 
			
		||||
        self.assertTrue('whitespace not allowed' in str(ctx.exception))
 | 
			
		||||
 | 
			
		||||
    def test_sub_zones(self):
 | 
			
		||||
        # NS for exactly the sub is allowed
 | 
			
		||||
        zone = Zone('unit.tests.', set(['sub', 'barred']))
 | 
			
		||||
        record = Record.new(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user