--- /dev/null
+Feature: Multiple instances
+ This feature tests whether multiple instances can be run, and whether
+ removing them does not affect the running of other instances
+
+ Scenario: Multiple instances
+ Given I have bind10 running with configuration multi_instance/multi_auth.config
+ And bind10 module Auth should be running
+ # this also checks whether the process is running according to Boss
+ If I remember the pid of process b10-auth
+ And remember the pid of process b10-auth-2
+
+ A query for example.com should have rcode REFUSED
+
+ When I remove bind10 configuration Boss/components value b10-auth-2
+
+ Then the pid of process b10-auth should not have changed
+ # From here on out it fails because of #1705
+ And a query for example.com should have rcode REFUSED
+
+ When I send bind10 the following commands
+ """
+ config add Boss/components b10-auth-2
+ config set Boss/components/b10-auth-2/special auth
+ config set Boss/components/b10-auth-2/process b10-auth
+ config set Boss/components/b10-auth-2/kind needed
+ config commit
+ """
+ And wait for new bind10 stderr message AUTH_SERVER_STARTED
+ And remember the pid of process b10-auth-2
+
+ Then the pid of process b10-auth should not have changed
+ A query for example.com should have rcode REFUSED
+
+ When I remove bind10 configuration Boss/components value b10-auth
+ Then the pid of process b10-auth-2 should not have changed
+ A query for example.com should have rcode REFUSED
from lettuce import *
import subprocess
import re
+import json
@step('start bind10(?: with configuration (\S+))?' +\
'(?: with cmdctl port (\d+))?' +\
"' was found in bindctl output:\n" +\
output
+def parse_bindctl_output_as_data_structure():
+ """Helper function for data-related command tests: evaluates the
+ last output of bindctl as a data structure that can then be
+ inspected.
+ If the bindctl output is not valid (json) data, this call will
+ fail with an assertion failure.
+ If it is valid, it is parsed and returned as whatever data
+ structure it represented.
+ """
+ # strip the 'Exit from bindctl' message. Why is it even there?
+ output = world.last_bindctl_stdout.replace("Exit from bindctl", "")
+ try:
+ return json.loads(output)
+ except ValueError as ve:
+ assert False, "Last bindctl output does not appear to be a " +\
+ "parseable data structure: " + str(ve)
+
+@step("remember the pid of process ([\S]+)")
+def remember_pid(step, process_name):
+ """Stores the PID of the process with the given name as returned by
+ Boss show_processes command.
+ Fails if the process with the given name does not appear to exist.
+ Stores the component_name->pid value in the dict world.process_pids.
+ This should only be used by the related step
+ 'the pid of process <name> should (not) have changed'
+ Arguments:
+ process name ('process <name>') the name of the component to store
+ the pid of.
+ """
+ step.given('send bind10 the command Boss show_processes')
+ running_processes = parse_bindctl_output_as_data_structure()
+ # show_processes output is a list of lists, where the inner lists
+ # are of the form [ pid, "name" ]
+ # Not checking data form; errors will show anyway (if these turn
+ # out to be too vague, we can change this)
+ found = False
+ if world.process_pids is None:
+ world.process_pids = {}
+ for process in running_processes:
+ if process[1] == process_name:
+ world.process_pids[process_name] = process[0]
+ found = True
+ assert found, "Process named " + process_name +\
+ " not found in output of Boss show_processes";
+
+@step('pid of process ([\S]+) should not have changed')
+def check_pid(step, process_name):
+ """Checks the PID of the process with the given name as returned by
+ Boss show_processes command.
+ Fails if the process with the given name does not appear to exist.
+ Fails if the process with the given name exists, but has a different
+ pid than it had when the step 'remember the pid of process' was
+ called.
+ Fails if that step has not been called (since world.process_pids
+ does not exist).
+ """
+ step.given('send bind10 the command Boss show_processes')
+ running_processes = parse_bindctl_output_as_data_structure()
+ # show_processes output is a list of lists, where the inner lists
+ # are of the form [ pid, "name" ]
+ # Not checking data form; errors will show anyway (if these turn
+ # out to be too vague, we can change this)
+ found = False
+ assert world.process_pids is not None, "No process pids stored"
+ assert process_name in world.process_pids, "Process named " +\
+ process_name +\
+ " was not stored"
+ for process in running_processes:
+ if process[1] == process_name:
+ assert world.process_pids[process_name] == process[0]
+ found = True
+ assert found, "Process named " + process_name +\
+ " not found in output of Boss show_processes";
+
+
@step('set bind10 configuration (\S+) to (.*)(?: with cmdctl port (\d+))?')
def config_set_command(step, name, value, cmdctl_port):
"""
"quit"]
run_bindctl(commands, cmdctl_port)
+@step('send bind10 the following commands(?: with cmdctl port (\d+))?')
+def send_multiple_commands(step, cmdctl_port):
+ """
+ Run bindctl, and send it the given multiline set of commands.
+ A quit command is always appended.
+ cmdctl_port ('with cmdctl port <portnr>', optional): cmdctl port to send
+ the command to. Defaults to 47805.
+ Fails if cmdctl does not exit with status code 0.
+ """
+ commands = step.multiline.split("\n")
+ # Always add quit
+ commands.append("quit")
+ run_bindctl(commands, cmdctl_port)
+
@step('remove bind10 configuration (\S+)(?: value (\S+))?(?: with cmdctl port (\d+))?')
def config_remove_command(step, name, value, cmdctl_port):
"""
["configurations/example.org.config.orig",
"configurations/example.org.config"],
["configurations/resolver/resolver_basic.config.orig",
- "configurations/resolver/resolver_basic.config"]
+ "configurations/resolver/resolver_basic.config"],
+ ["configurations/multi_instance/multi_auth.config.orig",
+ "configurations/multi_instance/multi_auth.config"]
]
# This is a list of files that, if present, will be removed before a scenario
# Convenience variable to access the last query result from querying.py
world.last_query_result = None
+ # For slightly better errors, initialize a process_pids for the relevant
+ # steps
+ world.process_pids = None
+
# Some tests can modify the settings. If the tests fail half-way, or
# don't clean up, this can leave configurations or data in a bad state,
# so we copy them from originals before each scenario