1
0
mirror of https://github.com/oskar456/dzonegit.git synced 2024-05-11 05:55:41 +00:00

35 Commits
v0.4 ... master

Author SHA1 Message Date
Ondřej Caletka
17cbd099de Version 0.15 2021-12-21 22:28:45 +01:00
Ondřej Caletka
d809e24172 Fixup tests for whitespace error handling 2021-12-21 22:24:17 +01:00
Dominik Pantůček
35d01a796e Apply whitespace check only on *.zone files. 2021-12-21 22:23:09 +01:00
sturmianseq
2f7776c0d1 Reverting the latest commit to avoid state pollution 2021-08-16 23:08:58 +02:00
sturmianseq
498d4a8b82 setup the correct pre-state for test_get_zone_name 2021-08-02 14:32:18 +02:00
Ondřej Caletka
a96597decb Migrate to GitHub Actions (#18)
* Migrate to GitHub Actions

* Drop Python 3.5 test

* Delete .travis.yml
2021-08-02 14:28:07 +02:00
Ondřej Caletka
8f952086aa Version 0.14 2020-05-26 11:12:38 +02:00
Ondřej Caletka
83a4049821 Fix tests when user.name and user.email are not defined
Fixes #15
2020-05-26 11:11:23 +02:00
Ondřej Caletka
15cdae67ee Merge pull request #14 from oskar456/zonerelfile 2020-05-17 11:27:54 +02:00
Ondřej Caletka
12fb932711 Version 0.13
Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
2020-05-17 11:24:29 +02:00
Ondřej Caletka
cb543514ac Document $zonerelfile template macro
Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
2020-05-17 11:23:51 +02:00
Przemyslaw Sztoch
f9c6a52357 New macro $zonerelfile in template file. 2020-05-17 11:18:06 +02:00
Ondřej Caletka
24d992d999 Version 0.12 2020-04-13 22:28:24 +02:00
Rob Seastrom
7cb7c42d76 change named-compilezone to use /usr/bin/env rather than absolute path 2020-04-13 22:20:54 +02:00
Ondřej Caletka
3dd346294a Merge pull request #9 from oskar456/travis
Use travis-ci.org as .com was not enabled yet
2019-08-19 15:34:31 +02:00
Ondřej Caletka
03fde74ede Use travis-ci.org as .com was not enabled yet 2019-08-19 15:30:34 +02:00
Michal Halenka
3769dd22fb Include Travis CI status in README 2019-08-19 15:00:49 +02:00
Michal Halenka
8d15bb531c Update .travis.yml
Add new python 3.7 release
Fix indentation
2019-08-19 15:00:26 +02:00
Ondřej Caletka
e2e4a3daf7 Version 0.11 2018-09-17 13:50:47 +02:00
Ondřej Caletka
4efef8be9e Do not template line breaks when header and footer are missing 2018-09-17 13:48:52 +02:00
Ondřej Caletka
ef059861b7 Version 0.10 - switch to Beta status 2018-08-28 12:45:08 +02:00
Ondřej Caletka
94461383e8 Add pre-commit check for missing trailing dot in PTR records. 2018-08-28 12:43:51 +02:00
Ondřej Caletka
3e09833ec1 Better handling of empty commit objects 2018-08-28 10:58:47 +02:00
Ondřej Caletka
9ad1e74a88 Fix no reconfig command issued on zone file rename. 2018-08-27 23:13:29 +02:00
Ondřej Caletka
a7d693253d version 0.9 (skipping version 0.8 as it was mistakenly published before) 2018-08-23 10:46:43 +02:00
Ondřej Caletka
3777453d2f Better handling of replace serial failure. 2018-08-23 10:38:56 +02:00
Ondřej Caletka
023906177a version 0.7 2018-08-20 16:37:49 +02:00
Ondřej Caletka
e79bb901f3 $UNIXTIME doc update 2018-08-20 16:37:08 +02:00
Ondřej Caletka
f07c84aa32 Fix no reload on very first push to the repository 2018-08-20 16:33:00 +02:00
Ondřej Caletka
62e35c59d0 version 0.6 2018-08-20 15:30:45 +02:00
Ondřej Caletka
806976ca6e Add smudge filter to replace $UNIXTIME directive on checkout 2018-08-20 15:30:45 +02:00
Ondřej Caletka
17f771bca6 Support custom $UNIXTIME directive in place of SOA serial 2018-08-20 15:30:45 +02:00
Ondřej Caletka
03cf26bbbe Add allowfancynames option 2018-08-20 15:30:40 +02:00
Ondřej Caletka
9923df14b7 Fix crash of post-receive when checkout directory does not exist 2018-08-16 10:35:07 +02:00
Ondřej Caletka
8d99e86222 Travis: allow failures of the nightly build 2018-08-15 14:09:57 +02:00
6 changed files with 276 additions and 60 deletions

42
.github/workflows/python-test.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python package
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Instal BIND 9 utils
run: sudo apt-get install -y bind9utils
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
python -m pip install -e .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest

View File

@@ -1,13 +0,0 @@
before_install:
- sudo apt-get install -y bind9utils
language: python
python:
- "3.5"
- "3.6"
- "nightly"
install:
- pip install -e .
- pip install pytest
script:
- pytest
sudo: false

View File

@@ -1,3 +1,6 @@
.. image:: https://travis-ci.org/oskar456/dzonegit.svg?branch=master
:target: https://travis-ci.org/oskar456/dzonegit
Git hooks to manage a repository of DNS zones
=============================================
@@ -15,6 +18,7 @@ Main features
- check if zone file compiles properly using `named-compilezone(8)`_
- autodetect zone name from file name or ``$ORIGIN`` directive
- enforce updating serial number when zone content is changed
- optional ``smudge`` filter to replace ``$UNIXTIME`` directive with current UNIX time
- both ``pre-commit`` and ``pre-receive``/``update`` hooks to enforce similar checks in the remote repository
- ``post-receive`` hook to checkout the working copy from a bare repository, generate config snippets for various DNS server software and reload them
- only Python 3.5+ standard library is used
@@ -53,6 +57,27 @@ Full instalation and usage
documentation on how to add custom hooks`_
- on the server, set up the configuration options for each repository
Support for $UNIXTIME directive
-------------------------------
If you want to use ``$UNIXTIME`` in your zone files instead of serial number,
you have to install a `smudge` filter on the server, that will replace the
directive with current unix time on every checkout. First, set up the filter
in the Git configuration:
.. code-block:: shell
$ git config --global filter.dzonegit.smudge $(which dzonegit-smudge-serial)
Then, apply the filter on all zone files using either ``.git/info/attributes``
or directly ``.gitattributes`` file inside the repository:
.. code-block::
*.zone filter=dzonegit
Configuration options
---------------------
@@ -63,10 +88,18 @@ named ``dzonegit``. All boolean options default to *False*.
*dzonegit.ignorewhitespaceerrors*
Ignore white space errors in ``pre-commit`` and ``pre-receive``/``update`` hooks.
*dzonegit.allowfancynames*
In ``pre-commit`` and ``pre-receive``/``update`` hooks, do not enforce zone
file name to be similar to the name of the zone.
*dzonegit.noserialupdate*
Do not try to automatically update zone serial number if necessary.
Valid only in the ``pre-commit`` hook.
*dzonegit.nomissingdotcheck*
Do not check for forgotten final dot on the right-hand side of PTR records.
Valid only in the ``pre-commit`` hook.
*dzonegit.checkoutpath*
Path to a writable directory, to which ``post-receive`` hook checks out
current *HEAD* after each update.
@@ -150,6 +183,9 @@ In the template strings, these placeholders are supported:
``$zonefile``
Full path to the zone file
``$zonerelfile``
Path to the zone file, relative to checkout path (useful for chroot environments)
``$zonevar``
Per-zone specific variable, see above

View File

@@ -41,24 +41,24 @@ class HookException(ValueError):
return "".join(r)
def get_head():
r = subprocess.run(
["git", "rev-parse", "--verify", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
if r.returncode == 0:
return r.stdout.decode("utf-8").strip()
else:
# Initial commit: diff against an empty tree object
return "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
def get_head(empty=False):
if not empty:
r = subprocess.run(
["git", "rev-parse", "--verify", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
if r.returncode == 0:
return r.stdout.decode("ascii").strip()
# Initial commit: diff against an empty tree object
return "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
def check_whitespace_errors(against, revision=None):
if revision:
cmd = ["git", "diff-tree", "--check", against, revision]
cmd = ["git", "diff-tree", "--check", against, revision, "*.zone"]
else:
cmd = ["git", "diff-index", "--check", "--cached", against]
cmd = ["git", "diff-index", "--check", "--cached", against, "*.zone"]
r = subprocess.run(
cmd,
stdout=subprocess.PIPE,
@@ -83,14 +83,44 @@ def get_file_contents(path, revision=None):
return r.stdout
def compile_zone(zonename, zonedata):
def unixtime_directive(zonedata, unixtime=None):
""" Filter binary zone data. Replace $UNIXTIME with current unix time. """
if unixtime is None:
unixtime = int(time.time())
return re.sub(
br'\$UNIXTIME\b',
str(unixtime).encode("ascii"),
zonedata,
flags=re.IGNORECASE,
)
def check_missing_trailing_dot(zonename, compiled_zonedata):
badlines = []
for line in compiled_zonedata.splitlines():
if re.search(
r"\sPTR\s+[^\s]*\.{}.$".format(zonename).encode("ascii"),
line,
re.I,
):
badlines.append(line.decode("utf-8"))
if badlines:
raise HookException(
"Possibly missing trailing dot after PTR records:\n{}".format(
"\n".join(badlines),
),
fname=zonename,
)
def compile_zone(zonename, zonedata, unixtime=None, missing_dot=False):
""" Compile the zone. Return tuple with results."""
CompileResults = namedtuple(
"CompileResults", "success, serial, zonehash, stderr",
)
r = subprocess.run(
["/usr/sbin/named-compilezone", "-o", "-", zonename, "/dev/stdin"],
input=zonedata,
["/usr/bin/env", "named-compilezone", "-o", "-", zonename, "/dev/stdin"],
input=unixtime_directive(zonedata, unixtime),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
@@ -98,6 +128,8 @@ def compile_zone(zonename, zonedata):
m = re.search(r"^zone.*loaded serial ([0-9]*)$", stderr, re.MULTILINE)
if r.returncode == 0 and m:
serial = m.group(1)
if missing_dot:
check_missing_trailing_dot(zonename, r.stdout)
zonehash = sha256(r.stdout).hexdigest()
return CompileResults(True, serial, zonehash, stderr)
else:
@@ -134,7 +166,7 @@ def get_altered_files(against, diff_filter=None, revision=None):
If revision is None, list changes between staging area and
revision. Otherwise differences between two revisions are computed.
"""
cmd = ["git", "diff", "--name-only", "-z"]
cmd = ["git", "diff", "--name-only", "-z", "--no-renames"]
if diff_filter:
cmd.append("--diff-filter={}".format(diff_filter))
if revision:
@@ -173,15 +205,15 @@ def get_zone_origin(zonedata):
def get_zone_name(path, zonedata):
"""
Try to guess zone name from either filename or the first $ORIGIN.
Throw a HookException if filename and zone ORIGIN differ more than
in slashes.
Unless disabled, throw a HookException if filename and zone ORIGIN differ
more than in slashes.
"""
stemname = Path(path).stem.lower()
originname = get_zone_origin(zonedata)
if originname:
tt = str.maketrans("", "", "/_,:-+*%^&#$")
sn, on = [s.translate(tt) for s in [stemname, originname]]
if sn != on:
if sn != on and not get_config("dzonegit.allowfancynames", bool):
raise HookException(
"Zone origin {o} differs from zone file.".format(o=originname),
fname=path,
@@ -191,15 +223,21 @@ def get_zone_name(path, zonedata):
return stemname
def check_updated_zones(against, revision=None, autoupdate_serial=False):
def check_updated_zones(
against,
revision=None,
autoupdate_serial=False,
missing_dot=False,
):
""" Check whether all updated zone files compile. """
unixtime = int(time.time())
for f in get_altered_files(against, "AMCR", revision):
if not f.suffix == ".zone":
continue
print("Checking file {f}".format(f=f))
zonedata = get_file_contents(f, revision)
zname = get_zone_name(f, zonedata)
rnew = compile_zone(zname, zonedata)
rnew = compile_zone(zname, zonedata, unixtime, missing_dot)
if not rnew.success:
raise HookException(
"New zone version does not compile",
@@ -208,7 +246,7 @@ def check_updated_zones(against, revision=None, autoupdate_serial=False):
try:
zonedata = get_file_contents(f, against)
zname = get_zone_name(f, zonedata)
rold = compile_zone(zname, zonedata)
rold = compile_zone(zname, zonedata, unixtime-1)
if (rold.success and rold.zonehash != rnew.zonehash and not
is_serial_increased(rold.serial, rnew.serial)):
@@ -219,9 +257,11 @@ def check_updated_zones(against, revision=None, autoupdate_serial=False):
if autoupdate_serial:
newserial = get_increased_serial(rnew.serial)
replace_serial(f, rnew.serial, newserial)
errmsg += " Serial has been automatically increased."
errmsg += " Check and recommit."
if replace_serial(f, rnew.serial, newserial):
errmsg += " Serial has been automatically increased."
errmsg += " Check and recommit."
else:
errmsg += " Autoupdate of serial number failed."
raise HookException(
errmsg,
fname=f,
@@ -264,8 +304,9 @@ def replace_serial(path, oldserial, newserial):
flags=re.DOTALL | re.IGNORECASE | re.MULTILINE,
)
if count != 1:
raise HookException("Cannot update zone serial number")
return False
path.write_text(updated)
return True
def get_zone_wildcards(name):
@@ -317,7 +358,8 @@ def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
out = list()
zones = dict()
mapping = {"datetime": datetime.datetime.now().strftime("%c")}
out.append(headertpl.substitute(mapping))
if headertpl.template:
out.append(headertpl.substitute(mapping))
for f in sorted(Path(checkoutpath).glob("**/*.zone")):
zonename = get_zone_name(f, f.read_bytes())
if whitelist and not any(
@@ -353,9 +395,10 @@ def template_config(checkoutpath, template, blacklist=set(), whitelist=set()):
zonevar = defaultvar
out.append(itemtpl.substitute(
mapping, zonename=zonename,
zonefile=str(f), zonevar=zonevar,
zonefile=str(f), zonerelfile=str(f.relative_to(checkoutpath)), zonevar=zonevar,
))
out.append(footertpl.substitute(mapping))
if footertpl.template:
out.append(footertpl.substitute(mapping))
return "\n".join(out)
@@ -369,13 +412,19 @@ def load_set_file(path):
}
def do_commit_checks(against, revision=None, autoupdate_serial=False):
def do_commit_checks(
against,
revision=None,
autoupdate_serial=False,
missing_dot=False,
):
try:
if not get_config("dzonegit.ignorewhitespaceerrors", bool):
check_whitespace_errors(against, revision=revision)
check_updated_zones(
against, revision=revision,
autoupdate_serial=autoupdate_serial,
missing_dot=missing_dot,
)
except HookException as e:
print(e)
@@ -385,7 +434,12 @@ def do_commit_checks(against, revision=None, autoupdate_serial=False):
def pre_commit():
against = get_head()
autoupdate_serial = not get_config("dzonegit.noserialupdate", bool)
do_commit_checks(against, autoupdate_serial=autoupdate_serial)
missing_dot = not get_config("dzonegit.nomissingdotcheck", bool)
do_commit_checks(
against,
autoupdate_serial=autoupdate_serial,
missing_dot=missing_dot,
)
def update(argv=sys.argv):
@@ -398,7 +452,7 @@ def update(argv=sys.argv):
refname, against, revision = argv[1:4]
if against == "0000000000000000000000000000000000000000":
against = get_head() # Empty commit
against = get_head(True) # Empty commit
if refname != "refs/heads/master":
raise SystemExit("Nothing else than master branch is accepted here")
@@ -416,7 +470,7 @@ def pre_receive(stdin=sys.stdin):
"is accepted here",
)
if against == "0000000000000000000000000000000000000000":
against = get_head() # Empty commit
against = get_head(True) # Empty commit
do_commit_checks(against, revision)
@@ -434,6 +488,7 @@ def post_receive(stdin=sys.stdin):
raise SystemExit("Checkout path not defined. Nothing to do.")
print("Checking out repository into {}".format(checkoutpath))
Path(checkoutpath).mkdir(parents=True, exist_ok=True)
subprocess.run(
["git", "checkout", "-f", "master"],
check=True,
@@ -465,7 +520,7 @@ def post_receive(stdin=sys.stdin):
if refname != "refs/heads/master":
continue
if against == "0000000000000000000000000000000000000000":
against = get_head() # Empty commit
against = get_head(True) # Empty commit
should_reconfig = [
f for f in get_altered_files(against, "ACDRU", revision)
if f.suffix == ".zone"
@@ -495,6 +550,15 @@ def post_receive(stdin=sys.stdin):
subprocess.run(cmd)
def smudge_serial(
bstdin=sys.stdin.buffer,
bstdout=sys.stdout.buffer,
unixtime=None,
):
"""Replace all $UNIXTIME directives with current unix time."""
bstdout.write(unixtime_directive(bstdin.read(), unixtime))
def get_action(argv=sys.argv):
name = Path(argv[0]).name
if "pre-commit" in name:
@@ -505,6 +569,8 @@ def get_action(argv=sys.argv):
return pre_receive
if "post-receive" in name:
return post_receive
if "smudge" in name:
return smudge_serial
def main():

View File

@@ -5,7 +5,7 @@ readme = Path(__file__).with_name("README.rst").read_text()
setup(
name="dzonegit",
version="0.4",
version="0.15",
description="Git hooks to manage a repository of DNS zones",
long_description=readme,
long_description_content_type="text/x-rst",
@@ -23,10 +23,11 @@ setup(
"dzonegit-pre-receive = dzonegit:pre_receive",
"dzonegit-post-receive = dzonegit:post_receive",
"dzonegit-update = dzonegit:update",
"dzonegit-smudge-serial = dzonegit:smudge_serial",
],
},
classifiers=[
"Development Status :: 3 - Alpha",
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",

View File

@@ -4,7 +4,7 @@ import subprocess
import time
import datetime
import os
from io import StringIO
from io import StringIO, BytesIO
from pathlib import Path
import dzonegit
@@ -15,6 +15,8 @@ def git_dir(tmpdir_factory):
d = tmpdir_factory.getbasetemp()
d.chdir()
subprocess.call(["git", "init"])
subprocess.call(["git", "config", "user.name", "dzonegit pytest"])
subprocess.call(["git", "config", "user.email", "nonexistent@example.com"])
return d
@@ -25,18 +27,22 @@ def test_get_head(git_dir):
subprocess.call(["git", "add", "dummy"])
subprocess.call(["git", "commit", "-m", "dummy"])
assert dzonegit.get_head() != "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
subprocess.call(["git", "update-ref", "-d", "HEAD"])
def test_check_whitespace_errors(git_dir):
git_dir.chdir()
git_dir.join("whitespace").write(" ")
subprocess.call(["git", "add", "whitespace"])
dzonegit.check_whitespace_errors(dzonegit.get_head())
git_dir.join("whitespace.zone").write(" ")
subprocess.call(["git", "add", "whitespace.zone"])
with pytest.raises(ValueError):
dzonegit.check_whitespace_errors(dzonegit.get_head())
subprocess.call(["git", "commit", "-m", "whitespace"])
with pytest.raises(ValueError):
dzonegit.check_whitespace_errors("HEAD~", dzonegit.get_head())
subprocess.call(["git", "rm", "-f", "whitespace"])
subprocess.call(["git", "rm", "-f", "whitespace*"])
subprocess.call(["git", "commit", "-m", "rm whitespace"])
dzonegit.check_whitespace_errors(dzonegit.get_head())
dzonegit.check_whitespace_errors("HEAD~", dzonegit.get_head())
@@ -62,16 +68,47 @@ $ORIGIN example.com.
60 IN NS ns
ns.example.com. 60 IN A 192.0.2.1
"""
r = dzonegit.compile_zone("example.org", testzone)
r = dzonegit.compile_zone("example.org", testzone, missing_dot=True)
assert not r.success
assert r.zonehash is None
assert r.stderr
r = dzonegit.compile_zone("example.com", testzone)
r = dzonegit.compile_zone("example.com", testzone, missing_dot=True)
assert r.success
assert r.serial == "1234567890"
assert r.zonehash
r2 = dzonegit.compile_zone("example.com", testzone + b"\n\n; some comment")
assert r.zonehash == r2.zonehash
testzone += b"1 60 IN PTR www\n"
dzonegit.compile_zone("example.com", testzone, missing_dot=False)
with pytest.raises(ValueError):
dzonegit.compile_zone("example.com", testzone, missing_dot=True)
def test_compile_unsmudged_zone():
testzone = b"""
$ORIGIN example.com.
@ 60 IN SOA ns hostmaster (
$UNIXTIME ; serial
3600 ; refresh (1 hour)
900 ; retry (15 minutes)
1814400 ; expire (3 weeks)
60 ; minimum (1 minute)
)
60 IN NS ns
ns.example.com. 60 IN A 192.0.2.1
"""
replaced = dzonegit.unixtime_directive(testzone)
assert b"$UNIXTIME" not in replaced
r = dzonegit.compile_zone("example.com", testzone, 123456)
assert r.success
assert r.serial == str(123456)
def test_smudge_serial():
bstdin = BytesIO(b"something $UNIXTIME something")
bstdout = BytesIO()
dzonegit.smudge_serial(bstdin, bstdout, 123456)
assert b"something 123456 something" == bstdout.getvalue()
def test_is_serial_increased():
@@ -91,7 +128,7 @@ def test_get_altered_files(git_dir):
assert files == {Path("dummy"), Path("new")}
# Refers to test_check_whitespace_errors
files = set(dzonegit.get_altered_files("HEAD~", "D", "HEAD"))
assert files == {Path("whitespace")}
assert files == {Path("whitespace"), Path("whitespace.zone")}
subprocess.call(["git", "checkout", "-f", "HEAD"])
assert set(dzonegit.get_altered_files("HEAD", "AM")) == set()
@@ -123,6 +160,7 @@ $ORIGIN eXample.com. ;coment
60 IN NS ns
ns.example.com. 60 IN A 192.0.2.1
"""
subprocess.call(["git", "config", "dzonegit.allowfancynames", "FALSE"])
assert "example.com" == dzonegit.get_zone_name(
"zones/example.com.zone", "",
)
@@ -131,6 +169,8 @@ ns.example.com. 60 IN A 192.0.2.1
)
with pytest.raises(ValueError):
dzonegit.get_zone_name("zones/example.org.zone", testzone)
subprocess.call(["git", "config", "dzonegit.allowfancynames", "TRUE"])
dzonegit.get_zone_name("zones/example.org.zone", testzone)
testzone = b"""
$ORIGIN 240/28.2.0.192.in-addr.arpa.
@ 60 IN SOA ns hostmaster 1 60 60 60 60
@@ -148,12 +188,12 @@ def test_replace_serial(git_dir):
@ 60 IN SOA ns hm 1 61 60 60 60
60 NS ns.example.org.
""")
dzonegit.replace_serial(Path("dummy.zone"), "1", "60")
assert dzonegit.replace_serial(Path("dummy.zone"), "1", "60")
assert git_dir.join("dummy.zone").read() == """
@ 60 IN SOA ns hm 60 61 60 60 60
60 NS ns.example.org.
"""
dzonegit.replace_serial(Path("dummy.zone"), "60", "61")
assert dzonegit.replace_serial(Path("dummy.zone"), "60", "61")
assert git_dir.join("dummy.zone").read() == """
@ 60 IN SOA ns hm 61 61 60 60 60
60 NS ns.example.org.
@@ -168,7 +208,7 @@ def test_replace_serial(git_dir):
)
60 NS ns.example.org.
""")
dzonegit.replace_serial(Path("dummy.zone"), "60", "6000000")
assert dzonegit.replace_serial(Path("dummy.zone"), "60", "6000000")
assert git_dir.join("dummy.zone").read() == """
@ 60 IN SOA ns hm (
6000000 ; serial
@@ -179,6 +219,7 @@ def test_replace_serial(git_dir):
)
60 NS ns.example.org.
"""
assert not dzonegit.replace_serial(Path("dummy.zone"), "0", "60")
def test_check_updated_zones(git_dir):
@@ -226,6 +267,29 @@ $ORIGIN dummy.
dzonegit.check_updated_zones("HEAD", autoupdate_serial=True)
subprocess.call(["git", "add", "dummy.zone"])
dzonegit.check_updated_zones(dzonegit.get_head())
git_dir.join("dummy.zone").write("""
$ORIGIN dummy.
@ 60 IN SOA ns hm $UNIXTIME 61 60 60 60
60 NS ns.example.org.
""")
subprocess.call(["git", "add", "dummy.zone"])
dzonegit.check_updated_zones(dzonegit.get_head())
subprocess.call(["git", "commit", "-m", "dummy.zone with $UNIXTIME"])
git_dir.join("dummy.zone").write("""
$ORIGIN dummy.
@ 60 IN SOA ns hm 1 60 60 60 60
60 NS ns.example.org.
""")
subprocess.call(["git", "add", "dummy.zone"])
with pytest.raises(ValueError):
dzonegit.check_updated_zones(dzonegit.get_head())
git_dir.join("dummy.zone").write("""
$ORIGIN dummy.
@ 60 IN SOA ns hm $UNIXTIME 60 60 60 60
60 NS ns.example.org.
""")
subprocess.call(["git", "add", "dummy.zone"])
dzonegit.check_updated_zones(dzonegit.get_head())
subprocess.call(["git", "commit", "-m", "final dummy.zone"])
dzonegit.check_updated_zones("HEAD~", "HEAD")
@@ -276,11 +340,11 @@ def test_post_receive(git_dir):
git_dir.chdir()
head = dzonegit.get_head()
revisions = "{} {} refs/heads/master\n".format(
"4b825dc642cb6eb9a060e54bf8d69288fbee4904",
"0000000000000000000000000000000000000000",
head,
)
stdin = StringIO(revisions)
codir = git_dir.mkdir("co")
codir = git_dir.join("co")
subprocess.call(["git", "config", "dzonegit.checkoutpath", str(codir)])
subprocess.call([
"git", "config", "dzonegit.reconfigcmd",
@@ -289,6 +353,17 @@ def test_post_receive(git_dir):
dzonegit.post_receive(stdin)
assert codir.join("dummy.zone").check()
assert codir.join("test").read() == "TEST\n"
# Test reconfig after renaming the file
codir.join("test").write("")
subprocess.call(["git", "mv", "dummy.zone", "dummy.zone.old"])
subprocess.call(["git", "commit", "-m", "rename dummy zone"])
revisions = "{} {} refs/heads/master\n".format(
head,
dzonegit.get_head(),
)
stdin = StringIO(revisions)
dzonegit.post_receive(stdin)
assert codir.join("test").read() == "TEST\n"
def test_template_config(git_dir):
@@ -319,6 +394,8 @@ def test_template_config(git_dir):
blacklist=set("*"),
)
assert " - zone: \"dummy\"\n file: \"" not in output
output = dzonegit.template_config(str(git_dir), "{}")
assert len(output) == 0
def test_load_set_file(git_dir):
@@ -332,3 +409,10 @@ def test_get_zone_wildcards():
"a.long.zone.name", "*.long.zone.name",
"*.zone.name", "*.name", "*",
]
def test_missing_trailing_dot():
zonename = "example.com"
zonedata = b"something.example.com. IN PTR s.example.com."
with pytest.raises(ValueError):
dzonegit.check_missing_trailing_dot(zonename, zonedata)