Browse Source

[1290] initial lettuce tests

plus a couple of fixes to make relative paths work in bind10 -c
Jelte Jansen 13 years ago
parent
commit
032f9633f4

+ 1 - 0
configure.ac

@@ -968,6 +968,7 @@ AC_OUTPUT([doc/version.ent
            src/lib/util/python/mkpywrapper.py
            src/lib/util/python/gen_wiredata.py
            src/lib/server_common/tests/data_path.h
+           tests/lettuce/setup_intree_bind10.sh
            tests/system/conf.sh
            tests/system/run.sh
            tests/system/glue/setup.sh

+ 1 - 2
src/bin/bind10/run_bind10.sh.in

@@ -45,6 +45,5 @@ export B10_FROM_BUILD
 BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
 export BIND10_MSGQ_SOCKET_FILE
 
-cd ${BIND10_PATH}
-exec ${PYTHON_EXEC} -O bind10 "$@"
+exec ${PYTHON_EXEC} -O ${BIND10_PATH}/bind10 "$@"
 

+ 7 - 6
src/lib/python/isc/config/cfgmgr.py

@@ -65,7 +65,7 @@ class ConfigManagerData:
             self.db_filename = file_name
             self.data_path = os.path.dirname(file_name)
         else:
-            self.db_filename = data_path + os.sep + file_name
+            self.db_filename = os.getcwd() + os.sep + file_name
             self.data_path = data_path
 
     def read_from_file(data_path, file_name):
@@ -117,12 +117,13 @@ class ConfigManagerData:
             if file:
                 file.close();
         return config
-        
+
     def write_to_file(self, output_file_name = None):
         """Writes the current configuration data to a file. If
            output_file_name is not specified, the file used in
            read_from_file is used."""
         filename = None
+
         try:
             file = tempfile.NamedTemporaryFile(mode='w',
                                                prefix="b10-config.db.",
@@ -291,7 +292,7 @@ class ConfigManager:
             # ok, just start with an empty config
             self.config = ConfigManagerData(self.data_path,
                                             self.database_filename)
-        
+
     def write_config(self):
         """Write the current configuration to the file specificied at init()"""
         self.config.write_to_file()
@@ -445,7 +446,7 @@ class ConfigManager:
             answer = ccsession.create_answer(1, "Wrong number of arguments")
         if not answer:
             answer = ccsession.create_answer(1, "No answer message from " + cmd[0])
-            
+
         return answer
 
     def _handle_module_spec(self, spec):
@@ -455,7 +456,7 @@ class ConfigManager:
         # todo: error checking (like keyerrors)
         answer = {}
         self.set_module_spec(spec)
-        
+
         # We should make one general 'spec update for module' that
         # passes both specification and commands at once
         spec_update = ccsession.create_command(ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
@@ -491,7 +492,7 @@ class ConfigManager:
         else:
             answer = ccsession.create_answer(1, "Unknown message format: " + str(msg))
         return answer
-        
+
     def run(self):
         """Runs the configuration manager."""
         self.running = True

+ 34 - 0
tests/lettuce/README

@@ -0,0 +1,34 @@
+BIND10 system testing with Lettuce
+or: to BDD or not to BDD
+
+In this directory, we define a set of behavioral tests for BIND 10. Currently,
+these tests are specific for BIND10, but we are keeping in mind that RFC-related
+tests could be separated, so that we can test other systems as well.
+
+Prerequisites:
+- Installed version of BIND 10 (but see below how to run it from source tree)
+- dig
+- lettuce (http://lettuce.it)
+
+To install lettuce, if you have the python pip installation tool, simply do
+pip install lettuce
+See http://lettuce.it/intro/install.html
+
+At this moment, we have a fixed port for local tests in our setups, port 47806.
+This port must be free. (TODO: can we make this run-time discovered?)
+
+Running the tests
+-----------------
+
+To run the tests, just run 'lettuce' in this directory.
+
+We have provided a script that sets up the shell environment to run the tests
+with the build tree version of bind. If your shell uses export to set
+environment variables, you can source the script setup_intree_bind10.sh, then
+run lettuce.
+
+
+Extending tests
+---------------
+
+[TODO]

+ 1 - 0
tests/lettuce/configurations/example.org.config

@@ -0,0 +1 @@
+{"version": 2, "Auth": {"database_file": "data/example.org.sqlite3", "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}}

+ 1 - 0
tests/lettuce/configurations/no_db_file.config

@@ -0,0 +1 @@
+{"version": 2, "Auth": {"database_file": "test.db", "listen_on": [{"port": 47806, "address": "127.0.0.1"}]}}

BIN
tests/lettuce/data/empty_db.sqlite3


BIN
tests/lettuce/data/example.org.sqlite3


+ 53 - 0
tests/lettuce/features/bind10_control.py

@@ -0,0 +1,53 @@
+from lettuce import *
+import subprocess
+
+@world.absorb
+def shutdown_server():
+    if world.bind10 is not None:
+        world.bind10.terminate()
+        world.bind10.wait()
+        world.bind10 = None
+
+@world.absorb
+def wait_for_output_lines(lines):
+    assert world.bind10 is not None
+    found = False
+    while not found:
+        output = world.bind10.stderr.readline()
+        for line in lines:
+            if output.find(line) != -1:
+                return line
+
+@step(u'start bind10(?: with configuration ([\w.]+))?')
+def start_bind10(step, config_file):
+    args = [ 'bind10', '-v' ]
+    if config_file is not None:
+        args.append('-c')
+        args.append("configurations/" + config_file)
+
+    world.bind10 = subprocess.Popen(args, 1, None, subprocess.PIPE,
+                                    subprocess.PIPE, subprocess.PIPE)
+    # check output to know when startup has been completed
+    # TODO what to do on failure?
+    message = world.wait_for_output_lines(["BIND10_STARTUP_COMPLETE",
+                                           "BIND10_STARTUP_ERROR"])
+    assert message == "BIND10_STARTUP_COMPLETE"
+
+@step(u'wait for bind10 auth to start')
+def wait_for_auth(step):
+    world.wait_for_output_lines(['AUTH_SERVER_STARTED'])
+
+@step(u'stop bind10')
+def stop_the_server(step):
+    world.shutdown_server()
+
+@step(u'set bind10 configuration (\S+) to (.*)')
+def set_config_command(step, name, value):
+    bindctl = subprocess.Popen(['bindctl'], 1, None, subprocess.PIPE,
+                               subprocess.PIPE, None)
+    bindctl.stdin.write("config set " + name + " " + value + "\n")
+    bindctl.stdin.write("config commit\n")
+    bindctl.stdin.write("quit\n")
+    bindctl.wait()
+
+

+ 42 - 0
tests/lettuce/features/querying.py

@@ -0,0 +1,42 @@
+from lettuce import *
+import subprocess
+import re
+
+#
+# define a class to easily access different parts
+# We may consider using our full library for this, but for now
+# simply store several parts of the response as text values in
+# this structure
+#
+# this will set 'rcode' as the result code, we 'define' one additional
+# rcode, "NO_ANSWER", if the dig process returned an error code itself
+# we will extend as necessary
+class QueryResult:
+    def __init__(self, name, qtype = None, qclass = None, port = 47806):
+        args = [ 'dig', '@localhost', '-p', str(port) ]
+        if qtype is not None:
+            args.append('-t')
+            args.append(str(qtype))
+        if qclass is not None:
+            args.append('-c')
+            args.append(str(qclass))
+        args.append(name)
+        dig_process = subprocess.Popen(args, 1, None, None, subprocess.PIPE,
+                                       None)
+        result = dig_process.wait()
+        if result != 0:
+            self.rcode = "NO_ANSWER"
+        else:
+            rcode_re = re.compile("status: ([A-Z]+)")
+            self.rcode = None
+            for out in dig_process.stdout:
+                rcode_match = rcode_re.search(out)
+                if rcode_match is not None:
+                    self.rcode = rcode_match.group(1)
+
+
+@step(u'A query for ([\w.]+) should have rcode ([\w.]+)')
+def query(step, query_name, rcode):
+    query_result = QueryResult(query_name)
+    assert query_result.rcode == rcode, "Got " + query_result.rcode
+

+ 27 - 0
tests/lettuce/features/server_from_sqlite3.feature

@@ -0,0 +1,27 @@
+Feature: SQLite3 backend
+    In order to support SQLite3
+    As administrators
+    We test serving an sqlite3 backend
+
+    Scenario: New database
+        Given I have no database
+        When I start bind10 with configuration no_db_file.config
+        Then wait for bind10 auth to start
+        Then stop bind10
+        I should see a database file
+
+    Scenario: example.org queries
+        When I start bind10 with configuration example.org.config
+        Then wait for bind10 auth to start
+        A query for www.example.com should have rcode REFUSED
+        A query for www.example.org should have rcode NOERROR
+        A query for doesnotexist.example.org should have rcode NXDOMAIN
+
+    Scenario: changing database
+        When I start bind10 with configuration example.org.config
+        Then wait for bind10 auth to start
+        A query for www.example.org should have rcode NOERROR
+        Then set bind10 configuration Auth/database_file to data/empty_db.sqlite3
+        A query for www.example.org should have rcode REFUSED
+        Then set bind10 configuration Auth/database_file to data/example.org.sqlite3
+        A query for www.example.org should have rcode NOERROR

+ 22 - 0
tests/lettuce/features/steps.py

@@ -0,0 +1,22 @@
+from lettuce import *
+import subprocess
+import os.path
+
+@before.each_scenario
+def initialize(feature):
+    # just make sure our cleanup won't fail if we never did
+    # run the bind10 instance
+    world.bind10 = None
+
+@after.each_scenario
+def cleanup(feature):
+    world.shutdown_server()
+
+@step(u'Given I have no database')
+def given_i_have_no_database(step):
+    assert not os.path.exists("test.db")
+
+@step(u'I should see a database file')
+def i_should_see_a_database_file(step):
+    assert os.path.exists("test.db")
+    os.remove("test.db")

+ 46 - 0
tests/lettuce/setup_intree_bind10.sh.in

@@ -0,0 +1,46 @@
+#! /bin/sh
+
+# Copyright (C) 2010  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
+export PYTHON_EXEC
+
+BIND10_PATH=@abs_top_builddir@/src/bin/bind10
+
+PATH=@abs_top_builddir@/src/bin/bind10:@abs_top_builddir@/src/bin/bindctl:@abs_top_builddir@/src/bin/msgq:@abs_top_builddir@/src/bin/auth:@abs_top_builddir@/src/bin/resolver:@abs_top_builddir@/src/bin/cfgmgr:@abs_top_builddir@/src/bin/cmdctl:@abs_top_builddir@/src/bin/stats:@abs_top_builddir@/src/bin/xfrin:@abs_top_builddir@/src/bin/xfrout:@abs_top_builddir@/src/bin/zonemgr:@abs_top_builddir@/src/bin/dhcp6:@abs_top_builddir@/src/bin/sockcreator:$PATH
+export PATH
+
+PYTHONPATH=@abs_top_builddir@/src/lib/python/isc/log_messages:@abs_top_builddir@/src/lib/python:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/xfr/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/python/isc/config:@abs_top_builddir@/src/lib/python/isc/acl/.libs:@abs_top_builddir@/src/lib/python/isc/datasrc/.libs
+export PYTHONPATH
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+SET_ENV_LIBRARY_PATH=@SET_ENV_LIBRARY_PATH@
+if test $SET_ENV_LIBRARY_PATH = yes; then
+	@ENV_LIBRARY_PATH@=@abs_top_builddir@/src/lib/dns/.libs:@abs_top_builddir@/src/lib/dns/python/.libs:@abs_top_builddir@/src/lib/cryptolink/.libs:@abs_top_builddir@/src/lib/cc/.libs:@abs_top_builddir@/src/lib/config/.libs:@abs_top_builddir@/src/lib/log/.libs:@abs_top_builddir@/src/lib/acl/.libs:@abs_top_builddir@/src/lib/util/.libs:@abs_top_builddir@/src/lib/util/io/.libs:@abs_top_builddir@/src/lib/exceptions/.libs:@abs_top_builddir@/src/lib/datasrc/.libs:$@ENV_LIBRARY_PATH@
+	export @ENV_LIBRARY_PATH@
+fi
+
+B10_FROM_SOURCE=@abs_top_srcdir@
+export B10_FROM_SOURCE
+# TODO: We need to do this feature based (ie. no general from_source)
+# But right now we need a second one because some spec files are
+# generated and hence end up under builddir
+B10_FROM_BUILD=@abs_top_builddir@
+export B10_FROM_BUILD
+
+BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
+export BIND10_MSGQ_SOCKET_FILE