Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[run]
omit = udns/__init__.py
tests/*

[html]
directory = coverage

[xml]
output = coverage.xml
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ dist
build
*.pyc
zones
.cache
coverage
.coverage
*.egg-info
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: python
services:
- redis-server
python:
- "2.7"
install:
- pip install -r requirements.txt
- pip install -r tests/requirements.txt
- pip install -e .
script:
- cram tests
- python setup.py test
after_success:
- coveralls
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
udns
====
udns - a micro (µ) DNS Server
=============================

[!['Stories in Ready'](https://badge.waffle.io/prologic/udns.png?label=ready&title=Ready)](https://waffle.io/prologic/udns)

[![Build Status](https://travis-ci.org/prologic/udns.svg)](https://travis-ci.org/prologic/udns)

[![Coverage](https://coveralls.io/repos/prologic/udns/badge.svg)](https://coveralls.io/r/prologic/udns)

[![Quality](https://landscape.io/github/prologic/udns/master/landscape.png)](https://landscape.io/github/prologic/udns/master)

udns is an authoritative, caching DNS server for development and small deployments written in [Python](http://python.org/) using the [circuits](http://circuitsframework.org/) Application Framework and the [dnslib](https://pypi.python.org/pypi/dnslib) DNS library. udns can be run standalone, via [Docker](http://docker.com/) or using the [Docker Compose](https://docs.docker.com/compose/) tool. udns is designed to be small, lightweight, fast and flexible. udns fully supports forwarding, caching as well as honoring TTL(s). udns will also read your `/etc/hosts` file at startup and use this to populate an internal hosts cache so that entries in your local `/etc/hosts` file are fully resolvable with tools such as `host`, `dig` and resolver client libraries.

Installation and Usage
Expand Down
16 changes: 14 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,25 @@
.. _Docker Compose: https://docs.docker.com/compose/


udns
====
udns - a micro (µ) DNS Server
=============================

.. image:: https://badge.waffle.io/prologic/udns.png?label=ready&title=Ready
:target: https://waffle.io/prologic/udns
:alt: 'Stories in Ready'

.. image:: https://travis-ci.org/prologic/udns.svg
:target: https://travis-ci.org/prologic/udns
:alt: Build Status

.. image:: https://coveralls.io/repos/prologic/udns/badge.svg
:target: https://coveralls.io/r/prologic/udns
:alt: Coverage

.. image:: https://landscape.io/github/prologic/udns/master/landscape.png
:target: https://landscape.io/github/prologic/udns/master
:alt: Quality

udns is an authoritative, caching DNS server for development and small
deployments written in `Python`_ using the `circuits`_ Application Framework
and the `dnslib`_ DNS library. udns can be run standalone, via `Docker`_
Expand Down
9 changes: 0 additions & 9 deletions bin/flushdb.py

This file was deleted.

15 changes: 0 additions & 15 deletions bin/test_models.py

This file was deleted.

4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
dnslib
circuits
cachetools

# Development version of circuits
-e git+https://github.com/circuits/circuits.git#egg=circuits

# Development version of redisco
-e git+https://github.com/kiddouk/redisco.git#egg=redisco-0.2.4
6 changes: 6 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Package: tests
# Date: 16th August 2014
# Author: James Mills, prologic at shortcircuit dot net dot au


"""Test Suite"""
48 changes: 48 additions & 0 deletions tests/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Test Client"""


from dnslib import DNSQuestion, DNSRecord, CLASS, QTYPE

from circuits import Event, Component
from circuits.net.events import write
from circuits.net.sockets import UDPClient


class query(Event):
"""query Event"""


class reply(Event):
"""reply Event"""


class DNS(Component):
"""DNS Protocol Handling"""

def read(self, peer, data):
self.fire(reply(DNSRecord.parse(data)))


class Client(Component):

channel = "client"

def init(self, server, port):
self.server = server
self.port = int(port)

self.transport = UDPClient(
("127.0.0.1", 0), channel=self.channel
).register(self)
self.protocol = DNS(channel=self.channel).register(self)

def reply(self, a):
self.a = a

def query(self, qname, qtype="A", qclass="IN"):
qtype = QTYPE.reverse[qtype]
qclass = CLASS.reverse[qclass]

q = DNSRecord(q=DNSQuestion(qname, qtype, qclass))

self.fire(write((self.server, self.port), q.pack()))
174 changes: 174 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
"""Test Configuration and Fixtures"""


import threading
from time import sleep
from collections import deque


from pytest import fixture

from circuits.core.manager import TIMEOUT
from circuits import handler, BaseComponent, Debugger, Manager


from udns.server import (
parse_args, parse_hosts, setup_database, setup_logging, Server
)

from .client import Client


class Watcher(BaseComponent):

def init(self):
self._lock = threading.Lock()
self.events = deque()

@handler(channel="*", priority=999.9)
def _on_event(self, event, *args, **kwargs):
with self._lock:
self.events.append(event)

def clear(self):
self.events.clear()

def wait(self, name, channel=None, timeout=6.0):
try:
for i in range(int(timeout / TIMEOUT)):
if channel is None:
with self._lock:
for event in self.events:
if event.name == name:
return True
else:
with self._lock:
for event in self.events:
if event.name == name and \
channel in event.channels:
return True

sleep(TIMEOUT)
finally:
self.events.clear()


class Flag(object):
status = False


class WaitEvent(object):

def __init__(self, manager, name, channel=None, timeout=6.0):
if channel is None:
channel = getattr(manager, "channel", None)

self.timeout = timeout
self.manager = manager

flag = Flag()

@handler(name, channel=channel)
def on_event(self, *args, **kwargs):
flag.status = True

self.handler = self.manager.addHandler(on_event)
self.flag = flag

def wait(self):
try:
for i in range(int(self.timeout / TIMEOUT)):
if self.flag.status:
return True
sleep(TIMEOUT)
finally:
self.manager.removeHandler(self.handler)


def pytest_addoption(parser):
parser.addoption(
"--dbhost", action="store", default="localhost",
help="Redis host to use for testing"
)


@fixture(scope="session")
def dbhost(request):
return request.config.getoption("--dbhost")


@fixture(scope="session")
def manager(request):
manager = Manager()

def finalizer():
manager.stop()

request.addfinalizer(finalizer)

waiter = WaitEvent(manager, "started")
manager.start()
assert waiter.wait()

if request.config.option.verbose:
verbose = True
else:
verbose = False

Debugger(events=verbose).register(manager)

return manager


@fixture(scope="session")
def watcher(request, manager):
watcher = Watcher().register(manager)

def finalizer():
waiter = WaitEvent(manager, "unregistered")
watcher.unregister()
waiter.wait()

request.addfinalizer(finalizer)

return watcher


@fixture(scope="session")
def server(request, manager, watcher, dbhost):
argv = ["-b", "0.0.0.0:5300", "--debug", "--dbhost", dbhost]

args = parse_args(argv)

logger = setup_logging(args)

db = setup_database(args, logger)

hosts = parse_hosts("/etc/hosts")

server = Server(args, db, hosts, logger)

server.register(manager)
watcher.wait("ready")

def finalizer():
server.unregister()

request.addfinalizer(finalizer)

return server


@fixture(scope="session")
def client(request, manager, watcher, server):
client = Client("127.0.0.1", 5300)

client.register(manager)
watcher.wait("ready")

def finalizer():
client.unregister()

request.addfinalizer(finalizer)

return client
31 changes: 31 additions & 0 deletions tests/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Tests Main"""


import sys
from types import ModuleType
from os.path import abspath, dirname
from subprocess import Popen, STDOUT


def importable(module):
try:
m = __import__(module, globals(), locals())
return type(m) is ModuleType
except ImportError:
return False


def main():
cmd = ["py.test", "-r", "fsxX", "--ignore=tmp"]

if importable("pytest_cov"):
cmd.append("--cov=udns")
cmd.append("--cov-report=html")

cmd.append(dirname(abspath(__file__)))

raise SystemExit(Popen(cmd, stdout=sys.stdout, stderr=STDOUT).wait())


if __name__ == "__main__":
main()
4 changes: 4 additions & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cram
pytest
coveralls
pytest-cov
Loading